-
Notifications
You must be signed in to change notification settings - Fork 15
FIDO2: Conformance testing server API
FIDO Alliance Certification Conformance Test Tools require companies to implement a standardised API for the conformance testing purpose.
- Introduction
-
Registration
- Overview
- Examples
- Credential Creation Options
- Authenticator Attestation Response
- Primary IDL
- ServerPublicKeyCredentialCreationOptionsRequest
- ServerPublicKeyCredentialCreationOptionsResponse
- ServerAuthenticatorAttestationResponse
- Supporting IDL
- ServerPublicKeyCredential
- ServerPublicKeyCredentialUserEntity
- ServerPublicKeyCredentialDescriptor
-
Authentication
- Overview
- Examples
- Credential Get Options
- Authenticator Assertion Response
- IDL
- ServerPublicKeyCredentialGetOptionsRequest
- ServerPublicKeyCredentialGetOptionsResponse
- ServerAuthenticatorAssertionResponse
-
Common
- IDL
- ServerResponse
- IDL
This document contains a non-normative, proposed REST API for FIDO2 servers. While this interface is not required, it is the interface that is used for the FIDO2 conformance test tools so that servers can receive and send messages in a standard way for those messages to be validated by the conformance test tools.
As with the FIDO2 specifications, the interfaces described here are highly dependent on the !WebAuthn specification. The nomenclature of this document follows that of WebAuthn and reuses the Interface Definition Language (IDL) for defining the messages that are sent to / from the server.
This document is broken up into three sections: registration, authentication, and common. The registration and authentication sections contain the messages relevant to those operations, and the common section includes messages and data formats that are common to both registration and authentication.
This section includes a brief overview of the registration messages that are exchanged between a client and the server, followed by examples of those messages, and concluding with IDL definitions of the messages. Note that registration is also referred to as "credential creation" due to the WebAuthn nomenclature.
The registration flow takes part in two steps for a total of four messages. The first step is that a client retrieves "Credential Creation Options", which involves the client sending a ServerPublicKeyCredentialCreationOptionsRequest to the server and the server responding with a ServerPublicKeyCredentialCreationOptionsResponse. These options are intended to be used with WebAuthn's navigator.credentials.create(), especially the challenge which necessarily is generated by the server for the sake of Man in the Middle (MITM) protection. Upon completion of navigator.credentials.create() the dictionary that is created from that call is sent back to the server as the ServerPublicKeyCredential with response field set to ServerAuthenticatorAttestationResponse. Note that the ServerAuthenticatorAttestationResponse extends the generic ServerAuthenticatorResponse, which is described in the Common section below. The server will validate challenges, origins, signatures and the rest of the ServerAuthenticatorAttestationResponse according to the algorithm described in section 7.1 of the [Webauthn] specs, and will respond with the appropriate ServerResponse message.
Request:
- URL: /attestation/options
-
Method:
POST - URL Params: None
-
Body:
application/jsonformattedServerPublicKeyCredentialCreationOptionsRequest
{
"username": "[email protected]",
"displayName": "John Doe",
"authenticatorSelection": {
"requireResidentKey": false,
"authenticatorAttachment": "cross-platform",
"userVerification": "preferred"
},
"attestation": "direct"
}Success Response:
-
HTTP Status Code:
200 OK -
Body:
application/jsonformattedServerPublicKeyCredentialCreationOptionsResponse
{
"status": "ok",
"errorMessage": "",
"rp": {
"name": "Example Corporation"
},
"user": {
"id": "S3932ee31vKEC0JtJMIQ",
"name": "[email protected]",
"displayName": "John Doe"
},
"challenge": "uhUjPNlZfvn7onwuhNdsLPkkE5Fv-lUN",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"timeout": 10000,
"excludeCredentials": [
{
"type": "public-key",
"id": "opQf1WmYAa5aupUKJIQp"
}
],
"authenticatorSelection": {
"requireResidentKey": false,
"authenticatorAttachment": "cross-platform",
"userVerification": "preferred"
},
"attestation": "direct"
}Error Response:
-
HTTP Status Code:
4xx or 5xx -
Body:
application/jsonformattedServerResponse
{
"status": "failed",
"errorMessage": "Missing challenge field!"
}Sample JavaScript:
fetch('/attestation/options', {
method : 'POST',
credentials : 'same-origin',
headers : {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
"username": "[email protected]",
"displayName": "John Doe",
"authenticatorSelection": {
"requireResidentKey": false,
"authenticatorAttachment": "cross-platform",
"userVerification": "preferred"
},
"attestation": "direct"
})
}).then(function (response) {
return response.json();
}).then(function (json) {
console.log(json);
}).catch(function (err) {
console.log({ 'status': 'failed', 'error': err });
})Request:
- URL: /attestation/result
-
Method:
POST - URL Params: None
-
Body:
application/jsonformattedServerPublicKeyCredentialwithresponsefield set toServerAuthenticatorAttestationResponse
{
"id": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
"response": {
"clientDataJSON": "eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9",
"attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"
},
"type": "public-key"
}Success Response:
-
HTTP Status Code:
200 OK -
Body:
application/jsonformattedServerResponse
{
"status": "ok",
"errorMessage": ""
}Error Response:
-
HTTP Status Code:
4xx or 5xx -
Body:
application/jsonformattedServerResponse
{
"status": "failed",
"errorMessage": "Can not validate response signature!"
}Sample Call:
fetch('/attestation/result', {
method : 'POST',
credentials : 'same-origin',
headers : {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
"id": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
"response": {
"clientDataJSON": "eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9",
"attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"
},
"type": "public-key"
})
}).then(function (response) {
return response.json();
}).then(function (json) {
console.log(json);
}).catch(function (err) {
console.log({ 'status': 'failed', 'error': err });
}) dictionary ServerPublicKeyCredentialCreationOptionsRequest {
required DOMString username;
required DOMString displayName;
AuthenticatorSelectionCriteria authenticatorSelection;
AttestationConveyancePreference attestation = "none";
};-
required
username- A human-readable name for the entity. For example, "alexm", "[email protected]" or "+14255551234". -
required
displayName- A human-friendly name for the user account, intended only for display. For example, "Alex P. Müller" or "田中 倫". -
authenticatorSelection- a dictionary containing AuthenticatorSelectionCriteria described in WebAuthn specification -
attestation- can be set to "none", "indirect", "direct". More in WebAuthn specification. Default set to none
dictionary ServerPublicKeyCredentialCreationOptionsResponse : ServerResponse {
required PublicKeyCredentialRpEntity rp;
required ServerPublicKeyCredentialUserEntity user;
required DOMString challenge;
required sequence<PublicKeyCredentialParameters> pubKeyCredParams;
unsigned long timeout;
sequence<ServerPublicKeyCredentialDescriptor> excludeCredentials = [];
AuthenticatorSelectionCriteria authenticatorSelection;
AttestationConveyancePreference attestation = "none";
AuthenticationExtensionsClientInputs extensions;
};- required
rp- a dictionary defined as PublicKeyCredentialRpEntity described in WebAuthn specification - required
user- a dictionary defined as ServerPublicKeyCredentialUserEntity, described in this document - required
challenge- a random base64url encoded challenge, that is minimum 16 bytes long, and maximum 64 bytes long - required
pubKeyCredParams- sequence of PublicKeyCredentialParameters described in WebAuthn specification -
timeout- timeout(ms) -
excludeCredentials- a sequence of ServerPublicKeyCredentialDescriptor described in this document -
authenticatorSelection- a dictionary set AuthenticatorSelectionCriteria described in WebAuthn specification -
attestation- can be set to "none", "indirect", "direct". More in WebAuthn specification. Default set to none -
extensions- a dictionary set to AuthenticationExtensionsClientInputs described in WebAuthn specs - Extends ServerResponse described in this document
Generally the same as AuthenticatorAttestationResponse from WebAuthn, but uses base64url encoding for fields that were of type BufferSource.
dictionary ServerAuthenticatorAttestationResponse : ServerAuthenticatorResponse {
required DOMString clientDataJSON;
required DOMString attestationObject;
};- required
clientDataJSON- base64url encoded clientDataJSON buffer - required
attestationObject- base64url encoded attestationObject buffer
Generally the same as PublicKeyCredential from WebAuthn, but uses base64url formatting for fields that are defined as BufferSource in WebAuthn.
dictionary ServerPublicKeyCredential : Credential {
required DOMString type;
required DOMString id;
required ServerAuthenticatorResponse response;
AuthenticationExtensionsClientOutputs getClientExtensionResults;
};- required
id- This attribute is inherited from Credential, though ServerPublicKeyCredential overrides it with base64url encoding of the authenticator credId - required
response- a dictionary defined as ServerAuthenticatorAttestationResponse or by ServerAuthenticatorAssertionResponse, described in this document - required
type- This attribute is inherited from Credential, though ServerPublicKeyCredential overrides it with "public-key" -
getClientExtensionResults- a map containing extension identifier, which contain client extension output entries produced by the extension’s client extension processing. - Extends Credential described in Credential Management API specification
Generally the same as the PublicKeyCredentialUserEntity from WebAuthn, but uses base64url formatting instead of BufferSource for id.
dictionary ServerPublicKeyCredentialUserEntity : PublicKeyCredentialEntity {
required DOMString id;
required DOMString displayName;
};- required
id- base64url encoded id buffer - required
displayName- A human-friendly name for the user account, intended only for display. For example, "Alex P. Müller" or "田中 倫". Corresponding to ServerPublicKeyCredentialCreationOptionsRequest.displayName - Extends PublicKeyCredentialEntity described in WebAuthn specification
Generally the same as PublicKeyCredentialDescriptor from WebAuthn, but uses base64url formatting instead of BufferSource for id.
dictionary ServerPublicKeyCredentialDescriptor {
required PublicKeyCredentialType type;
required DOMString id;
sequence<AuthenticatorTransport> transports;
};- required
type- a dictionary defined as PublicKeyCredentialType described in WebAuthn specification - required
id- contains base64url encoded credential ID of the public key credential that the caller is referring to. - transports - a sequence of AuthenticatorTransport described in WebAuthn specification
This section starts with an overview of the messages exchanged with the server for authentication, then proceeds to show examples of those messages, and concludes with the specific IDL definitions of those messages. Note that "authentication" is sometimes referred to as "getting credentials", a "credential request", or "getting an authentication assertion" due to the terminology used in WebAuthn.
Similar to the communication flow described for Registration, the Authentication flow requires four messages to be exchanged with the server. The first pair of messages are a request from the client to the server in the format of ServerPublicKeyCredentialGetOptionsRequestand the server returns a corresponding ServerPublicKeyCredentialGetOptionsResponse to the client. This ServerPublicKeyCredentialGetOptionsResponse is intended to be used as the parameters to the WebAuthn navigator.credentials.get() call. The results of navigator.credentials.get() are formatted by the client in to a ServerPublicKeyCredential with response field set to ServerAuthenticatorAssertionResponse and sent to the server. The server validates the assertion according the section 7.2 of the [WebAuthn] specification, and returns the corresponding ServerResponse.
Request:
- URL: /attestation/options
-
Method:
POST - URL Params: None
-
Body:
application/jsonencodedServerPublicKeyCredentialGetOptionsRequest
{
"username": "[email protected]",
"userVerification": "required"
}Success Response:
-
HTTP Status Code:
200 OK -
Body:
application/jsonencodedServerPublicKeyCredentialGetOptionsResponse
{
"status": "ok",
"errorMessage": "",
"challenge": "6283u0svT-YIF3pSolzkQHStwkJCaLKx",
"timeout": 20000,
"rpId": "example.com",
"allowCredentials": [
{
"id": "m7xl_TkTcCe0WcXI2M-4ro9vJAuwcj4m",
"type": "public-key"
}
],
"userVerification": "required"
}Error Response:
-
HTTP Status Code:
4xx or 5xx -
Body:
application/jsonencodedServerResponse
{
"status": "failed",
"errorMessage": "User does not exists!"
}Sample Call:
fetch('/attestation/options', {
method : 'POST',
credentials : 'same-origin',
headers : {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
"username": "[email protected]",
"userVerification": "required"
})
}).then(function (response) {
return response.json();
}).then(function (json) {
console.log(json);
}).catch(function (err) {
console.log({ 'status': 'failed', 'error': err });
})Request:
- URL: /assertion/result
-
Method:
POST - URL Params: None
-
Body:
application/jsonencodedServerPublicKeyCredentialwithresponsefield set toServerAuthenticatorAssertionResponse
{
"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
"response":{
"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAAA",
"signature":"MEYCIQCv7EqsBRtf2E4o_BjzZfBwNpP8fLjd5y6TUOLWt5l9DQIhANiYig9newAJZYTzG1i5lwP-YQk9uXFnnDaHnr2yCKXL",
"userHandle":"",
"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ4ZGowQ0JmWDY5MnFzQVRweTBrTmM4NTMzSmR2ZExVcHFZUDh3RFRYX1pFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9"
},
"type":"public-key"
}Success Response:
-
HTTP status code:
200 OK -
Body:
application/jsonencodedServerResponse
{
"status": "ok",
"errorMessage": ""
}Error Response:
-
HTTP status code:
4xx or 5xx -
Body:
application/jsonencodedServerResponse
{
"status": "failed",
"errorMessage": "Can not validate response signature!"
}Sample Call:
fetch('/assertion/result', {
method : 'POST',
credentials : 'same-origin',
headers : {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
"response":{
"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAAA",
"signature":"MEYCIQCv7EqsBRtf2E4o_BjzZfBwNpP8fLjd5y6TUOLWt5l9DQIhANiYig9newAJZYTzG1i5lwP-YQk9uXFnnDaHnr2yCKXL",
"userHandle":"",
"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ4ZGowQ0JmWDY5MnFzQVRweTBrTmM4NTMzSmR2ZExVcHFZUDh3RFRYX1pFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9"
},
"type":"public-key"
})
}).then(function (response) {
return response.json();
}).then(function (json) {
console.log(json);
}).catch(function (err) {
console.log({ 'status': 'failed', 'error': err });
}) dictionary ServerPublicKeyCredentialGetOptionsRequest {
required DOMString username;
UserVerificationRequirement userVerification = "preferred";
};-
required
username- A human-readable name for the entity. For example, "alexm", "[email protected]" or "+14255551234". -
userVerification- can be set to "required", "preferred", "discouraged". More in WebAuthn specification. Default set to "preferred"
dictionary ServerPublicKeyCredentialGetOptionsResponse : ServerResponse {
required DOMString challenge;
unsigned long timeout;
USVString rpId;
sequence<ServerPublicKeyCredentialDescriptor> allowCredentials = [];
UserVerificationRequirement userVerification = "preferred";
AuthenticationExtensionsClientInputs extensions;
};- required
challenge- a random base64url encoded challenge, that is minimum 16 bytes long, and maximum 64 bytes long -
timeout- timeout(ms) -
rpId- This optional member specifies the relying party identifier claimed by the caller. If omitted, its value will be the CredentialsContainer object’s relevant settings object's origin's effective domain. -
allowCredentials- a sequence of ServerPublicKeyCredentialDescriptor described in this document -
userVerification- can be set to "required", "preferred", "discouraged". More in WebAuthn specification. Default set to "preferred". Corresponds to ServerPublicKeyCredentialGetOptionsRequest.userVerification -
extensions- a dictionary set to AuthenticationExtensionsClientInputs described in WebAuthn specs - Extends ServerResponse described in this document
dictionary ServerAuthenticatorAssertionResponse : ServerAuthenticatorResponse {
required DOMString clientDataJSON;
required DOMString authenticatorData;
required DOMString signature;
required DOMString userHandle;
};- required
clientDataJSON- base64url encoded clientDataJSON buffer - required
authenticatorData- base64url encoded authenticatorData buffer - required
signature- base64url encoded signature buffer - required
userHandle- base64url encoded userHandle buffer. Corresponding to registered user ServerPublicKeyCredentialUserEntity.id
dictionary ServerResponse {
required Status status;
required DOMString errorMessage = "";
}- required
status- Describing the status of the response. Can be set to either "ok" or "failed". - required
errorMessage- Ifstatusis set to "failed" this field MUST NOT be empty