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:
- Send your access key or intent token in the Authorization Bearer token header. For more information on the intent token, see Intent endpoint.
- Set the
channel
parameter tofido2
. - Add the
username
oruserID
parameter. If you are using discoverable credentials, skip this step. - Optionally, set the
fido2Options.userVerification
topreferred
,required
, ordiscouraged
. With these settings you can customize the requirement for user verification. The default value ispreferred
.
HTTP request example
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"
}
}
Start polling the status endpoint
Use the approval response for status polling.
To verify the success of the approval operation, start polling the status
endpoint from your frontend application. The recommended polling interval is 1.5 seconds. Depending on your circumstances, such as user needs and production system capabilities, you might need to adjust the wait times between two polling cycles.
For detailed information on the HTTP request parameters and response fields, see the Status endpoint page of the API reference documentation.
Send the POST https://{instance}.mauth.nevis.cloud/api/v1/status
call with your instance ID, and configure the HTTP request as follows:
Send the
statusToken
that was retrieved during the approval operation.- Poll for the status as long as
status
is pending.
When you receive a succeeded
, failed
, or unknown
status, you can react accordingly. If the status is succeeded
, continue with verifying the operation from your backend application.
HTTP request examples
- cURL
- Python 3
# Set $statusToken
curl "https://$instance.mauth.nevis.cloud/api/v1/status" \
-XPOST \
-H 'Content-Type: application/json' \
-d "{ \"statusToken\": \"$statusToken\" }"
data = {'statusToken': 'eyJhb...lc3g'}
resp = requests.post(f'https://{instance}.mauth.nevis.cloud/api/v1/status', json = data)
print(resp.json())
HTTP response examples
- Status is pending
- Status is succeeded
- Status is failed
- Status is unknown
200 OK
: Pending - The operation is still waiting for confirmation by the user
{
"transactionId": "b7d44592-91f3-4f4b-9e37-202a3061edc3",
"status": "pending",
"userId": "7e16ba00-92e2-4fcb-b30e-1af8fdc843aa",
"username": "Userxyz123",
"token": "eyJhb...Fl9bpEXGGw",
"createdAt": "2020-10-09T12:52:48Z",
"lastUpdatedAt": "2020-10-09T12:52:48Z"
}
200 OK
: Succeeded - The operation was successfully confirmed by the user
{
"transactionId": "b7d44592-91f3-4f4b-9e37-202a3061edc3",
"status": "succeeded",
"userId": "7e16ba00-92e2-4fcb-b30e-1af8fdc843aa",
"username": "Userxyz123",
"token": "eyJhb...Fl9bpEXGGw",
"createdAt": "2020-10-09T12:52:48Z",
"lastUpdatedAt": "2020-10-09T12:52:48Z"
}
412 Precondition Failed
: The transaction was aborted or timed out
{
"transactionId": "b7d44592-91f3-4f4b-9e37-202a3061edc3",
"status": "failed",
"userId": "7e16ba00-92e2-4fcb-b30e-1af8fdc843aa",
"username": "Userxyz123",
"token": "eyJhb...Fl9bpEXGGw",
"createdAt": "2020-10-09T12:52:48Z",
"lastUpdatedAt": "2020-10-09T12:52:48Z"
}
404 Not Found
: The provided status token is not known
{
"status": "unknown"
}
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:
- Using the @github/webauthn-json client-side Javascript library which is a convenience wrapper for the WebAuthn API.
- Checking if WebAuthn is supported by the browser.
- Creating the
authenticateOptions
object that will be needed by the@github/webauthn-json
library. TheauthenticateOptions
object must contain theuserId
field, except when discoverable credentials are used. - Calling the
@github/webauthn-json
library to authenticate with the WebAuthn credential. As a result, aserverPublicKeyCredential
object is returned.noteAfter 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. - Extending
ServerPublicKeyCredential
with theuserAgent
attribute to provide Authentication Cloud with vital information. - Setting the
userHandle
manually, using theuserId
, ifuserHandle
is not present in the credential object. - Sending the credentials to the Authentication Cloud backend assertion endpoint. This endpoint does not require a token.
- Handling the success or failure response, based on the assertion endpoint response.
JavaScript template
// 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-instance>.mauth.nevis.cloud/_app/assertion/result', {
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 server response to your application backend
The server response must be sent to your application backend to verify the operation from the server side.
Verify the operation
Once the authentication is successful and you receive the server response, verify the operation. Use of the following verification methods:
- HTTP request to the
status
endpoint - HTTP request to the
introspect
endpoint
Send an HTTP request to the status endpoint
By calling the status
endpoint from your backend application, you request Authentication Cloud to verify the validity and the authenticity of the operation. This is done by using the statusToken
from the previous HTTP response.
For detailed information on the HTTP request parameters and response fields, see the Status endpoint page of the API reference documentation.
Send the POST https://{instance}.mauth.nevis.cloud/api/v1/status
call with your instance ID and include the statusToken
that was retrieved during the operation.
Send an HTTP request to the introspect endpoint
By calling the introspect
endpoint from your backend application, you request Authentication Cloud to verify the validity and the authenticity of the user. This is done by using the transaction token returned by the status
endpoint.
For detailed information on the HTTP request parameters and response fields, see the Introspect endpoint page of the API reference documentation.
Send the POST https://{instance}.mauth.nevis.cloud/api/v1/introspect
call with your instance ID, and configure the HTTP request to the introspect
endpoint as follows:
- Send your access key in the Authorization Bearer token header.
- Add your token that you want to verify.
HTTP request examples
- cURL
- Python 3
# Set $token
curl "https://$instance.mauth.nevis.cloud/api/v1/introspect" \
-XPOST \
-H "Authorization: Bearer $access_key" \
--data-urlencode "token=$token"
data = {'token': 'eyJhb...lc3g'}
resp = requests.post(f'https://{instance}.mauth.nevis.cloud/api/v1/introspect',
headers = {'authorization': f'Bearer {access_key}'},
data = data)
print(resp.json())
HTTP response examples
- Active and valid access key
- Valid transaction token
- Valid status token
- Valid intent token
- Invalid token
200 OK
: Active and valid access key
{
"active": true,
"iat": 1642436165000,
"sub": "c8edb1d1-6dac-470f-b2fb-c25277a5c5b6",
"aud": "api",
"iss": "https://{instance}.mauth.nevis.cloud/"
}
200 OK
: Valid transaction token
{
"active": true,
"iat": 1654759374000,
"sub": "fc6bf4bb-9f46-48a9-95d6-91051a3d6468",
"aud": "transaction",
"iss": "https://{instance}.mauth.nevis.cloud/"
}
200 OK
: Valid status token
{
"active": true,
"iat": 1662625372000,
"sub": "29886449-532e-4de7-923d-845e94b81762",
"aud": "status",
"iss": "https://{instance}.mauth.nevis.cloud/",
"jti": "fdc3e23e-9ec8-4375-9b13-9a586270a9cd"
}
200 OK
: Valid intent token
{
"iat": 1662626062,
"exp": 1662626662,
"aud": "intent",
"iss": "https://{instance}.mauth.nevis.cloud/",
"sub": "0afb658e-9536-430f-a69b-d53a7a603be1",
"scope": "enroll:sms,app"
}
200 OK
: Invalid token
{
"active": false
}