Skip to main content

Register a credential

In this guide you will learn how to create a credential with the Hanko API in just three simple steps.

The Registration ceremony#

We assume that a user has already created an account with your service (the relying party).

  • When a user requests registration of a credential, your relying party application communicates basic user info to the Hanko Authentication API.
  • The API generates credential creation options, including a challenge to which the user must respond using a cryptographically signed statement identifying the user.
  • For this purpose, the user provides consent to generate a public/private credential key pair on the authenticator device using a local authorization gesture (e.g. through a PIN or biometrics).
  • The private key is associated with the relying party and stored on a secure hardware element on the authenticator device. Unlike a password, the private key is never transmitted over the network. The public key is sent to the Hanko Authentication API along with the signed statement.
  • The Hanko Authentication API validates the statement and stores the public key, associating it with a user ID.

Step 1: Initialize Registration at Hanko API#

The first step to registering a credential is to initialize the registration ceremony. Initialization consists of retrieving credential creation options from the Hanko API in order to trigger credential creation on an authenticator through the Web Authentication API.

Frontend - Trigger registration initialization#

In your frontend, trigger registration initialization by first making a request to your application backend. To process the request you will need to implement a handler. In this case, we assume your backend handler responds to requests issued to the /webauthn/registration/initialize endpoint:

async function initializeRegistration() {
const response = await fetch('/webauthn/registration/initialize');
if (!response.ok) {
// handle error
}
return response.json();
}

Backend - Retrieve credential creation options#

Your backend handler must then issue a POST request to the registration initialization endpoint of the Hanko API (/v1/webauthn/registration/initialize- see also the API reference). The request body must include data about the user entity on whose behalf registration is performed. The Hanko API does not manage user entities so user data must originate from an existing relying party user store. In this guide we assume a user already has an existing account and valid session with your application such that your backend is able to retrieve data from the user store and can construct an appropriate request body. The user data object used as a request body must include:

  • an id: this is used to map a credential to a specific user account. The user id must not contain personally identifying information.
  • a name and a displayName: these are human-readable descriptions of the user that are used for display purposes only
Example registration initialization request body - /v1/webauthn/registration/initialize
{
"user": {
"id": "e3be22a7-13cf-4235-a09c-380dfd44ac04",
"name": "John Doe",
"displayName": "johndoe123"
}
}
note

During registration initialization you can also specify additional options for the registration operation. For simplicity, this guide will not make use of these options and use only parameters that are required for creating a credential. In a more advanced implementation relying parties can

On successful initialization the Hanko API will respond with the generated credentialCreationOptions. You will use these in the next step to trigger credential creation through the Web Authentication API.

Example JSON response: credential creation options
{
"publicKey": {
"challenge": "POjDn1rKqktubP8F9wkIdW_QbrFayNPwSUG9JF_P9IM",
"rp": {
"name": "Hanko GmbH",
"icon": "https://hanko.io/logo.png",
"id": "hanko.io"
},
"user": {
"name": "john.doe@example.com",
"displayName": "John Doe",
"id": "ZTNiZTIyYTctMTNjZi00MjM1LWEwOWMtMzgwZGZkNDRhYzA0"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -35
},
{
"type": "public-key",
"alg": -36
},
{
"type": "public-key",
"alg": -257
},
{
"type": "public-key",
"alg": -258
},
{
"type": "public-key",
"alg": -259
},
{
"type": "public-key",
"alg": -37
},
{
"type": "public-key",
"alg": -38
},
{
"type": "public-key",
"alg": -39
},
{
"type": "public-key",
"alg": -8
}
],
"authenticatorSelection": {},
"timeout": 10000
}
}

Step 2: Create the credentials#

In order to create a new credential, your application frontend must pass the credentialCreationOptions obtained in the previous step to the browser through the Web Authentication API. To make things as convenient as possible, you can use the Hanko JavaScript SDK to accomplish this. First, install the SDK:

Include the SDK as a script in your HTML:

<script src="https://cdn.jsdelivr.net/npm/@teamhanko/hanko-webauthn@latest/dist/browser-global/hanko-webauthn.browser-global.js"></script>

This will set a browser global hankoWebAuthn variable.

Then use the SDK to create a credential through the WebAuthn API:

// Get credentialCreationOptions (Step 1)
const credentialCreationOptions = await initializeRegistration()
// Create the credentials using the credentialCreationOptions
const webAuthnResponse = await hankoWebAuthn.create(credentialCreationOptions)

This will trigger a native browser dialog prompting the user to perform an authorization gesture. The dialog prompts differ depending on the available and connected authenticators. Once a user has given consent by performing the gesture, the authenticator generates a public/private credential key pair and returns the public key as part of the response.

Step 3: Finalize registration with the Hanko API#

The last step is to finalize the registration with the Hanko API.

Frontend - Forward Web Authentication API response#

Pass the webAuthnResponse from the previous step to your application backend. Again, you will need a corresponding backend handler. Implement a handler that processes requests issued to the /webauthn/registration/finalize endpoint.

// Finalize the registration using the `webAuthnResponse` from Step 2
async function finalizeRegistration(webAuthnResponse) {
const response = await fetch('/webauthn/registration/finalize', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(webAuthnResponse)
});
if (!response.ok) {
// handle fetch error
}
return response.json();
}
Full example using plain JavaScript
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@teamhanko/hanko-webauthn@latest/dist/browser-global/hanko-webauthn.browser-global.js"></script>
</head>
<body>
<script type="text/javascript">
async function register() {
try {
const credentialCreationOptions = await initializeRegistration();
const webAuthnResponse = await hankoWebAuthn.create(credentialCreationOptions);
return await finalizeRegistration(webAuthnResponse);
} catch (error) {
// handle error
}
}
async function initializeRegistration() {
const response = await fetch('/webauthn/registration/initialize');
if (!response.ok) {
// handle fetch error
}
return response.json();
}
async function finalizeRegistration(webAuthnResponse) {
const response = await fetch('/webauthn/registration/finalize', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(webAuthnResponse)
});
if (!response.ok) {
// handle fetch error
}
return response.json();
}
</script>
</body>
</html>
Full example using JavaScript modules
import {create as createCredential} from '@teamhanko/hanko-webauthn';
async function register() {
try {
const credentialCreationOptions = await initializeRegistration();
const webAuthnResponse = await createCredential(credentialCreationOptions);
return await finalizeRegistration(webAuthnResponse);
} catch (error) {
// handle error
}
}
async function initializeRegistration() {
const response = await fetch('/webauthn/registration/initialize');
if (!response.ok) {
// handle fetch error
}
return response.json();
}
async function finalizeRegistration(webAuthnResponse) {
const response = await fetch('/webauthn/registration/finalize', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(webAuthnResponse)
});
if (!response.ok) {
// handle fetch error
}
return response.json();
}

Backend - Finalize with the Hanko API#

Finalize the registration by forwarding the response sent by your frontend to the Hanko API using a POST request to the registration finalization endpoint (/v1/webauthn/registration/finalize, see also the API reference).

Example registration finalization request body - /v1/webauthn/registration/finalize
{
"type": "public-key",
"id": "9vmRvoxvs2tpqi9VCvInW7TOu98",
"rawId": "9vmRvoxvs2tpqi9VCvInW7TOu98",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV...",
"attestationObject": "o2NmbXRkbm9uZWdh..."
}
}

On successful finalization, the Hanko API returns a representation of the registered credential. This credential data can be used, for example, for display and management of credentials in a user profile. Congratulations, you have registered your first credential with the Hanko API!

Example JSON response: created credential
{
"credential": {
"id": "AUKybEpjFAmx-IcvgF...",
"createdAt": "2021-05-18T14:16:45.551437Z",
"lastUsed": "2021-05-18T14:16:45.551435Z",
"name": "Initial Name",
"userVerification": true,
"isResidentKey": false,
"authenticator": {
"aaguid": "adce0002-35bc-c60a-648b-0b25f1f05503",
"attachment": "platform"
},
"user": {
"id": "e3be22a7-13cf-4235-a09c-380dfd44ac04"
}
}
}

Try the example application#

Are you looking for a more immersive learning experience? See how it all works by trying out our example application.