Skip to main content

Authenticate with FIDO2

Once a FIDO2-capable authenticator is registered, it can be used for authentication. To integrate the Authentication Cloud functions into your application, you must send an HTTP request to the approval endpoint, then use the approval response and the previously created WebAuthn credential to prompt an authentication dialog for the user. Our JavaScript solution can handle the communication between the WebAuthn API and the Authentication Cloud API.

The following diagram shows the end-to-end sequence of an authentication operation using a FIDO2 authenticator. The steps that must be performed to integrate the Authentication Cloud into your application are in bold. Some steps are included in the JavaScript solution.

Send an HTTP request to the approval endpoint

For detailed information on the HTTP request parameters and response fields, see the Approval endpoint page of the API reference documentation.

Send the POST https://{instance}.mauth.nevis.cloud/api/v1/approval call with your instance ID, and configure the HTTP request as follows:

  1. Send your access key or intent token in the Authorization Bearer token header. For more information on the intent token, see Intent endpoint.
  2. Set the channel parameter to fido2.
  3. Add the username or userID parameter. If you are using discoverable credentials, skip this step.
  4. Optionally, set the fido2Options.userVerification to preferred, required, or discouraged. With these settings you can customize the requirement for user verification. The default value is preferred.

HTTP request example

cURL code sample for approval with custom FIDO2 options
curl "https://$instance.mauth.nevis.cloud/api/v1/approval"  \
-XPOST \
-H "Authorization: Bearer $access_key" \
-H 'Content-Type: application/json;charset=utf-8' \
-d "{ \"username\":\"$username\",
\"channel\":\"fido2\",
\"fido2Options\": {
\"userVerification\":\"required\"
}
}"

HTTP response example

201 Created: Approval using a FIDO2 device

{
"statusToken": "eyJ...iJ9.ey...fVag0LMfTMX5kQ",
"transactionId": "8b2373-...-9698e35e",
"userId": "d517-...-7cb4521b3",
"credentialRequestOptions": {
"allowCredentials": [
{
"id": "hZAe...aNBS7rX8jBY",
"type": "public-key"
}
],
"challenge": "2LpI-1C...drA",
"rpId": "nevis-latest-dev-cbbc98.mauth.nevis.cloud",
"timeout": 60000,
"userVerification": "required"
}
}

Forward the credentialRequestOptions object

The approval response contains the userId and the credentialRequestOptions object, which are required by the WebAuthn API for authentication with the FIDO2 credential.

Authenticate with the WebAuthn credential using the JavaScript solution

Once the credentialRequestOptions is forwarded to the browser, apply the Authentication Cloud JavaScript template to forward the WebAuthn credential. The frontend of the relying party must include a JavaScript solution to connect to the Authentication Cloud API.

The template includes the following WebAuthn calls:

  1. Using the @github/webauthn-json client-side Javascript library which is a convenience wrapper for the WebAuthn API.
  2. Checking if WebAuthn is supported by the browser.
  3. Creating the authenticateOptions object that will be needed by the @github/webauthn-json library. The authenticateOptions object must contain the userId field, except when discoverable credentials are used.
  4. Calling the @github/webauthn-json library to authenticate with the WebAuthn credential. As a result, a serverPublicKeyCredential object is returned.
    note

    After this step, a native browser dialog prompts the user to perform the authorization gesture. Once a user has given consent by doing so, the authenticator generates an assertion and a ServerPublicKeyCredential object is returned.

  5. Extending ServerPublicKeyCredential with the userAgent attribute to provide Authentication Cloud with vital information.
  6. Setting the userHandle manually, using the userId, if userHandle is not present in the credential object.
  7. Sending the updatedServerPublicKeyCredential to your application backend.
  8. Handling the success or failure response, based on the assertion endpoint response.

JavaScript template

JavaScript template for forwarding the credential
// 0
import {
create,
parseCreationOptionsFromJSON,
get,
parseRequestOptionsFromJSON,
} from "https://unpkg.com/@github/[email protected]/dist/esm/webauthn-json.browser-ponyfill.js";

...
function defaultHeaders() {
return {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=utf-8',
};
}

function isWebAuthnNotSupportedByTheBrowser() {
if (window.PublicKeyCredential === undefined || typeof window.PublicKeyCredential !== 'function') {
let errorMessage = 'Oh no! This browser doesn\'t currently support WebAuthn.';
if (window.location.protocol === 'http:' && (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1')){
errorMessage = 'WebAuthn only supports secure connections. For testing over HTTP, you can use the origin "localhost".';
}
console.log(errorMessage);
return true;
} else {
return false;
}
}

// 1
if (isWebAuthnNotSupportedByTheBrowser()) {
// case when the browser does not support WebAuthn
}

// 2
const authenticateOptions = {
publicKey: credentialRequestOptions
};

// 3
const parsedCredentialRequestOptions = parseRequestOptionsFromJSON(authenticateOptions);
const serverPublicKeyCredential = (await get(parsedCredentialRequestOptions)).toJSON();

// 4
serverPublicKeyCredential.userAgent = navigator.userAgent;

// 5
// According to the WebAuthn specification, userHandle is optional. Some authenticators return null.
if (serverPublicKeyCredential.response.userHandle) {
updatedServerPublicKeyCredential.response.userHandle = serverPublicKeyCredential.response.userHandle;
}
// Workaround: if the userId is known, fill the userHandle because the Authentication Cloud backend expects it.
else if (userId) {
updatedServerPublicKeyCredential.response.userHandle = btoa(userId);
}

// 6
const response = await fetch(
'https://<your-backend-url>', {
method: 'POST',
credentials: 'same-origin',
headers: defaultHeaders(),
body: JSON.stringify(updatedServerPublicKeyCredential),
});
const result = await response.json();

// 7
if (result.status === 'ok') {
// handle success
} else {
// handle failure, you can find more details in result.errorMessage
}

Forward the authentication response to the assertion endpoint

Send the authentication response to the Authentication Cloud assertion API endpoint. This endpoint does not require a token.