Introduction
Blisache is a web service providing Fido2/webauthn authentication. It acts as a relying party for webauthn registration and authentication ceremonies. Passkeys and non-resident keys are both supported.
We provide client-side packages to help the integration of the API.
API endpoints are available at https://blisache.com/api/<api_endpoint>.
The API is built over HTTP and is composed of 4 main blocks which group endpoints by features :
- Registration
- Authentication
- Credential manipulation
- Signature verification
Each of these and their specific endpoints are documented in their own reference chapter.
ES256 and EdDSA signature algorithms are currently supported.
Registering your domain
The service is enabled for a domain by creating an account and registering your domain name on the admin website.
The website itself integrates the features exposed by the API. You create an account by registering a key using a Fido2 compatible device. In practice nearly all smartphones and many laptop have already this capability through fingerprint or facial recognition. Otherwise a security key can be used as well.
After creating an account and authenticating successfully, you get redirected to the domains page. This is where you can see your already registered domains and register new ones.
When registering a domain, an activation code is generated and needs to be copied in the domain’s DNS TXT records.
blisache-verification=myactivationkeyhere
A background task will regularly check for this activation code in the DNS TXT records of the domain and disable the service in case it is not found.
At that point the service is activated for your domain and you can start integrating the API.
Setup a local environment
Working with Blisache locally requires you have registered and activated your domain. In this example I will create the subdomain dev.blisache.com and I will activate this subdomain by setting the blisache-provided activation key in the DNS TXT records for my new subdomain. Domain activation can take some time because DNS records needs to propagate. You can replace every occurence of dev.blisache.com with your own domain.
Your registered domain and the domain serving your pages must match. Also you need to serve over SSL to enable a secure context when nagivating with the browser. Your browser will most likely consider self-signed certificates as insecure, therefore you will need to generate a real certificate for your test domain. Of course when running locally you don’t really serve from a domain. We will configure an instance of Nginx as proxy to do so. Install Nginx and add this configuration to /etc/nginx/conf.d/dev.blisache.com.conf :
# /etc/nginx/conf.d/dev.blisache.com.conf
server {
listen 443 ssl;
server_name dev.blisache.com; # replace with your own domain here
ssl_certificate /etc/letsencrypt/live/dev.blisache.com/fullchain.pem; # replace with your own cert path
ssl_certificate_key /etc/letsencrypt/live/dev.blisache.com/privkey.pem; # replace with your own key path
location / {
proxy_pass http://localhost:8888; # replace with your own port here
}
}
Don’t forget to restart your nginx instance.
Finally you have to configure a local DNS resolution so that your test domain resolves to your local machine. This is acheived with your hosts file /etc/hosts :
# /etc/hosts
127.0.0.1 localhost dev.blisache.com
Now my app is served at the url https://dev.blisache.com and is ready to start testing and integrating the Blisache API.
Integrating the API
API is available at https://blisache.com/api/<api_endpoint>.
We provide client-side packages to help integrating the API :
- blisache-client provides functions for interacting with the API
- blisache-html provides HTML integration built on top of
blisache-client
Blisache-client
blisache-client is a wasm artifact exposing the API as simple javascript functions to be called. It can be imported in your project like this :
import * as blisache from "blisache-client";
Here are the prototypes of the exposed functions :
function register(login: string, name: string): Promise<any>;
register() initiates a new registration to the API, prompts the authenticator for a new key, and registers the created key to the API. Thats mean we create a new account or new user.
function authenticate(login?: string | null): Promise<any>;
authenticate() initiates a new authentication to the API, prompts the authenticator for an already registered key and authenticates the user to the API.
function credential_list(login?: string | null): Promise<any>;
credential_list() authenticates the user with authenticate() and retreives the credentials registered to the API for that user.
function credential_add(login: string, name: string): Promise<any>;
credential_add() authenticates the user with authenticate() and registers a new key for that user using register().
function credential_revoke(login?: string | null, credential_id: bigint): Promise<any>;
credential_revoke() authenticates the user with authenticate() and unregisters the user’s target credential.
The login is the account name. It is often set as the email address of the user. It is sent to the API and anonymised for storage.
The name is the name of the user. It is stored in the authenticator and used as display name. It is not sent to the API.
login and name are mandatory in registration-related endpoints.
In authentication-related endpoints login is optional. If login present, a server-side credential search is performed. When login is absent, a discoverable resident key (passkey) is selected for the ceremony with possible user prompt.
Each of these functions returns a SignedResponse. This is a data object containing a message and its associated signature. The message is the json response from the API serialized as a bytes array. The signature is the signature of the message signed from Blisache servers also serialized as a bytes array. A SignedResponse wraps success and error responses from the API and should be sent to your backend for further processing.
Blisache-html
blisache-html is a javascript package providing handlers to be hooked as html <form> submit event listener.
In the example below you register the function registerHandleSubmit as submit event listener on all elements tagged with blisacheRegister class.
import { registerHandleSubmit } from "blisache-html";
document.querySelectorAll('.blisacheRegister').forEach(form => {
form.addEventListener("submit", registerHandleSubmit);
});
Then you define your registration form :
<form action="/register" method="post" class="blisacheRegister">
<input type="email" name="email" data-blisache-login />
<input type="text" name="name" data-blisache-name />
<input type="text" name="custom_data" />
<button type="submit">Register</button>
</form>
When this form is submited the registerHandleSubmit event handler is called. Data from the form inputs are fetched through the custom attributes data-blisache-login and data-blisache-name and are passed to the register() function from blisache-client. There is a data-blisache-credential_id custom attribute available to wire data to the credential_id field of the credential_revoke() function.
The register() function outputs message and signature as bytes arrays.
The handler then appends two new inputs to your form - blisache_message and blisache_signature - containing respectively message and signature data base64 encoded.
The form is then sent to your backend’s endpoint for processing. Notice that you can add more data to your form like the additional custom_data input. When receiving this form on server-side there will be 5 fields :
emailnamecustom_datablisache_messageblisache_signature
You need to verify blisache_signature against blisache_message to be sûre that data comes from blisache servers and has not been altered.
blisache_message and blisache_signature then can be base64 decoded and json deserialized according to the BlisacheResponse or BlisacheErrorResponse formats.
Server-side processing
There are few things to check when receiving SignedResponse data (blisache_message and blisache_signature) on your backend’s processing endoint.
First you want to verify blisache_message against blisache_signature to be sûre the message has not been tempered with. You can either :
- Download Blisache public keys using the
get public keyendpoint and verify the signature on your own. - Send your
SignedResponseto theverify signatureendpoint and get a success or error response.
Then you will have to base64 decode blisache_message to get the raw json response. That json response deserializes to either BlisacheResponse<T>, T being dependant on the called endpoint, or BlisacheErrorResponse if an error happened.
In both cases there will be some action, result and timestamp fields. You will want to check that action match your intent, that result is a success and that timestamp is not too old. The appreciation of not too old is up to you but the general idea is to protect against replay attacks.
HTTP endpoints
Registration
Authentication
Credential list, add, revoke
Register
Registration is a two-step process using the registration init and registration complete endpoints.
The provided blisache-client takes care of calling the endpoints in order.
Register init
Calling the init endpoint starts a new registration and returns data to be fed to the authenticator.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /register/init | application/json | A RegisterInitRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<RegisterInitResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Register complete
After the authenticator has generated a new credential, you need to feed back these data throught the complete endpoint.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /register/complete | application/json | A RegisterCompleteRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<RegisterCompleteResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Authenticate
Authentication is a two-step process using the authentication init and authentication complete endpoints.
The provided blisache-client takes care of calling the endpoints in order.
Authenticate init
Calling the init endpoint starts a new authentication and returns data to be fed to the authenticator.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /authenticate/init | application/json | A AuthenticateInitRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<AuthenticateInitResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Authenticate complete
After the authenticator has retreived the desired credential, you need to feed back these data throught the complete endpoint.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /authenticate/complete | application/json | A AuthenticateCompleteRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<AuthenticateCompleteResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Credential list
Listing credentials is a two-step process using the authentication init and list credentials endpoints.
Listing credentials starts like an authentication procedure by calling the authentication init endpoint but as a second step you call the list credential endpoint.
Credential list init
This is an authenticate init request.
Credential list complete
After the authenticator has retreived the desired credential, you need to feed back these data throught the complete endpoint.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /credential/list | application/json | A AuthenticateCompleteRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<CredentialListResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Credential add
Adding a credential is a three-step process using the authentication init, add credential and register complete endpoints.
Start like an authentication procedure by calling the authentication init endpoint but as a second step you call the add credential endpoint.
The challenge must be resolved using an existing credential.
The add credential endpoint returns a register init response whose challenge must be signed using the new credential to register.
Then call the registration complete endpoint to end the procedure.
Credential add init
This is an authenticate init request.
Credential add
The add credential endpoint returns a register init response whose challenge must be signed using the new credential to register. Then call the registration complete endpoint to end the procedure.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /credential/add | application/json | A AuthenticateCompleteRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<RegisterInitResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Credential add complete
This is a register complete request.
Credential revoke
Revoking credentials is a two-step process using the credential revoke init and credential revoke complete endpoints.
Credential revoke init
Calling the init endpoint starts a new revocation and returns data to be fed to the authenticator.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /credential/revoke/init | application/json | A CredentialRevokeInitRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<AuthenticateInitResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Credential revoke complete
After the authenticator has retreived the desired credential, you need to feed back these data throught the complete endpoint.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /credential/revoke/complete | application/json | A AuthenticateCompleteRequest data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignedResponse enclosing a BlisacheResponse<AuthenticateCompleteResponse> |
| 400 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A SignedResponse enclosing a BlisacheErrorResponse signaling an internal server error |
Signature validation
Signature validation includes an endpoint for getting the blisache public key in order to verify a signature on your own and also a convenience endpoint asking the verification of a message by the blisache backend.
Get public key
Getting the blisache public key in order to verify a signature on your own.
Request
| Method | Path |
|---|---|
| GET | /signature/public_key |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A PublicKeyResponse |
| 500 | application/json | A BlisacheErrorResponse signaling an internal server error |
Verify signature
A convenience endpoint asking the verification of a message by the blisache backend.
Request
| Method | Path | Content-type | Body |
|---|---|---|---|
| POST | /signature/verify | application/json | A SignedResponse data object |
Responses
| Code | Content-type | Body |
|---|---|---|
| 200 | application/json | A SignatureVerifyResponse |
| 400 | application/json | A BlisacheErrorResponse signaling an error in the input data |
| 422 | application/json | A BlisacheErrorResponse signaling a functional error |
| 500 | application/json | A BlisacheErrorResponse signaling an internal server error |
Data structures
Here are the json schemas of the data structures involved in the API.
SignedResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SignedResponse",
"type": "object",
"properties": {
"message": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"signature": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"message",
"signature"
]
}
BlisacheResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "BlisacheResponse",
"type": "object",
"properties": {
"action": {
"$ref": "#/$defs/BlisacheAction"
},
"data": {
"oneOf": [
{ "$ref": "/RegisterInitResponse" },
{ "$ref": "/RegisterCompleteResponse" },
{ "$ref": "/AuthenticateInitResponse" },
{ "$ref": "/AuthenticateCompleteResponse" },
{ "$ref": "/CredentialListResponse" }
]
},
"domain": {
"type": "string"
},
"result": {
"$ref": "#/$defs/BlisacheActionResult"
},
"timestamp": {
"type": "integer",
"format": "uint64",
"minimum": 0
}
},
"required": [
"timestamp",
"domain",
"action",
"result",
"data"
],
"$defs": {
"BlisacheAction": {
"type": "string",
"enum": [
"RegisterInit",
"RegisterComplete",
"AuthenticateInit",
"AuthenticateComplete",
"ListCredentials",
"AddCredential",
"RevokeCredentialInit",
"RevokeCredentialComplete",
"GetPublicKey",
"VerifySignature"
]
},
"BlisacheActionResult": {
"type": "string",
"enum": [
"Success",
"Error"
]
}
}
}
BlisacheErrorResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "BlisacheErrorResponse",
"type": "object",
"properties": {
"action": {
"$ref": "#/$defs/BlisacheAction"
},
"caused_by": {
"$ref": "#/$defs/ErrorDescription"
},
"result": {
"$ref": "#/$defs/BlisacheActionResult"
},
"timestamp": {
"type": "integer",
"format": "uint64",
"minimum": 0
}
},
"required": [
"timestamp",
"action",
"result",
"caused_by"
],
"$defs": {
"BlisacheAction": {
"type": "string",
"enum": [
"RegisterInit",
"RegisterComplete",
"AuthenticateInit",
"AuthenticateComplete",
"ListCredentials",
"AddCredential",
"RevokeCredentialInit",
"RevokeCredentialComplete",
"GetPublicKey",
"VerifySignature"
]
},
"BlisacheActionResult": {
"type": "string",
"enum": [
"Success",
"Error"
]
},
"ErrorCategory": {
"type": "string",
"enum": [
"Input",
"Functional",
"RateLimit",
"Technical"
]
},
"ErrorDescription": {
"type": "object",
"properties": {
"category": {
"$ref": "#/$defs/ErrorCategory"
},
"detail": {
"type": "string"
}
},
"required": [
"category",
"detail"
]
}
}
}
RegisterInitRequest
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "RegisterInitRequest",
"type": "object",
"properties": {
"login": {
"type": "string"
},
"origin": {
"type": "string"
}
},
"required": [
"login",
"origin"
]
}
RegisterInitResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "RegisterInitResponse",
"type": "object",
"properties": {
"domain": {
"type": "string"
},
"registration_data": {
"$ref": "#/$defs/RegistrationData"
},
"user": {
"$ref": "#/$defs/User"
}
},
"required": [
"user",
"domain",
"registration_data"
],
"$defs": {
"AttestationConveyancePreference": {
"type": "string",
"enum": [
"None",
"Indirect",
"Direct",
"Enterprise"
]
},
"AuthenticatorAttachment": {
"type": "string",
"enum": [
"Any",
"Platform",
"CrossPlatform"
]
},
"PubKeyCredParam": {
"type": "object",
"properties": {
"alg": {
"type": "integer",
"format": "int64"
},
"type": {
"type": "string"
}
},
"required": [
"alg",
"type"
]
},
"RegistrationData": {
"type": "object",
"properties": {
"attestation_conveyance_preference": {
"$ref": "#/$defs/AttestationConveyancePreference"
},
"authenticator_attachment": {
"$ref": "#/$defs/AuthenticatorAttachment"
},
"challenge": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"id": {
"type": "integer",
"format": "int64"
},
"public_key_credential_params": {
"type": "array",
"items": {
"$ref": "#/$defs/PubKeyCredParam"
}
},
"resident_key": {
"$ref": "#/$defs/ResidentKeyRequirement"
},
"user_id_validation_constraint": {
"$ref": "#/$defs/UserIdValidationConstraint"
},
"user_verification": {
"$ref": "#/$defs/UserVerificationRequirement"
}
},
"required": [
"id",
"challenge",
"authenticator_attachment",
"resident_key",
"user_verification",
"attestation_conveyance_preference",
"user_id_validation_constraint",
"public_key_credential_params"
]
},
"ResidentKeyRequirement": {
"type": "string",
"enum": [
"Discouraged",
"Preferred",
"Required"
]
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"login_hash": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"id",
"login_hash"
]
},
"UserIdValidationConstraint": {
"type": "string",
"enum": [
"None",
"Email"
]
},
"UserVerificationRequirement": {
"type": "string",
"enum": [
"Required",
"Preferred",
"Discouraged"
]
}
}
}
RegisterCompleteRequest
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "RegisterCompleteRequest",
"type": "object",
"properties": {
"credential": {
"$ref": "#/$defs/AttestationPublicKeyCredential"
},
"id": {
"type": "integer",
"format": "int64"
},
"login_hash": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"id",
"login_hash",
"credential"
],
"$defs": {
"AttestationPublicKeyCredential": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"raw_id": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"response": {
"$ref": "#/$defs/AuthenticatorAttestationResponse"
},
"type": {
"type": "string"
}
},
"required": [
"id",
"raw_id",
"response",
"type"
]
},
"AuthenticatorAttestationResponse": {
"type": "object",
"properties": {
"attestation_object": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"client_data_json": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"attestation_object",
"client_data_json",
"transports"
]
}
}
}
RegisterCompleteResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "RegisterCompleteResponse",
"type": "object",
"properties": {
"user_id": {
"type": "integer",
"format": "int64"
},
"user_login_hash": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"user_login_hash",
"user_id"
]
}
AuthenticateInitRequest
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "AuthenticateInitRequest",
"type": "object",
"properties": {
"login": {
"type": [
"string",
"null"
]
},
"origin": {
"type": "string"
}
},
"required": [
"origin"
]
}
AuthenticateInitResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "AuthenticateInitResponse",
"type": "object",
"properties": {
"authentication_data": {
"$ref": "#/$defs/AuthenticationData"
},
"domain": {
"type": "string"
},
"user_login_hash": {
"type": [
"array",
"null"
],
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"domain",
"authentication_data"
],
"$defs": {
"AuthenticateAllowCredential": {
"type": "object",
"properties": {
"id": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
},
"type": {
"type": "string"
}
},
"required": [
"id",
"transports",
"type"
]
},
"AuthenticationData": {
"type": "object",
"properties": {
"allowed_credentials": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/AuthenticateAllowCredential"
}
},
"challenge": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"id": {
"type": "integer",
"format": "int64"
},
"user_verification": {
"$ref": "#/$defs/UserVerificationRequirement"
}
},
"required": [
"id",
"challenge",
"user_verification"
]
},
"UserVerificationRequirement": {
"type": "string",
"enum": [
"Required",
"Preferred",
"Discouraged"
]
}
}
}
AuthenticateCompleteRequest
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "AuthenticateCompleteRequest",
"type": "object",
"properties": {
"credential": {
"$ref": "#/$defs/AuthenticationPublicKeyCredential"
},
"id": {
"type": "integer",
"format": "int64"
},
"login_hash": {
"type": [
"array",
"null"
],
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"id",
"credential"
],
"$defs": {
"AuthenticationPublicKeyCredential": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"raw_id": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"response": {
"$ref": "#/$defs/AuthenticatorAssertionResponse"
},
"type": {
"type": "string"
}
},
"required": [
"id",
"raw_id",
"response",
"type"
]
},
"AuthenticatorAssertionResponse": {
"type": "object",
"properties": {
"authenticator_data": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"client_data_json": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"signature": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"user_handle": {
"type": [
"array",
"null"
],
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
}
},
"required": [
"authenticator_data",
"client_data_json",
"signature"
]
}
}
}
AuthenticateCompleteResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "AuthenticateCompleteResponse",
"type": "object",
"properties": {
"sign_count_warning": {
"type": "boolean"
},
"user_id": {
"type": "integer",
"format": "int64"
}
},
"required": [
"user_id",
"sign_count_warning"
]
}
CredentialListResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "CredentialListResponse",
"type": "object",
"properties": {
"keys": {
"type": "array",
"items": {
"$ref": "#/$defs/Credential"
}
},
"user_id": {
"type": "integer",
"format": "int64"
}
},
"required": [
"user_id",
"keys"
],
"$defs": {
"Credential": {
"type": "object",
"properties": {
"backup_eligibility": {
"type": "boolean"
},
"backup_state": {
"type": "boolean"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"credential_type": {
"type": "string"
},
"fingerprint": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"maximum": 255,
"minimum": 0
}
},
"id": {
"type": "integer",
"format": "int64"
},
"last_used_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"sign_count": {
"type": "integer",
"format": "int64"
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
},
"user_verified": {
"type": "boolean"
}
},
"required": [
"id",
"fingerprint",
"credential_type",
"sign_count",
"user_verified",
"transports",
"backup_eligibility",
"backup_state",
"created_at"
]
}
}
}
CredentialRevokeInitRequest
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "CredentialRevokeInitRequest",
"type": "object",
"properties": {
"credential_id": {
"type": "integer",
"format": "int64"
},
"login": {
"type": [
"string",
"null"
]
},
"origin": {
"type": "string"
}
},
"required": [
"credential_id",
"origin"
]
}
PublicKeyResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PublicKeyResponse",
"type": "array",
"items": {
"$ref": "#/$defs/PublicKeyResponseData"
},
"$defs": {
"PublicKeyResponseData": {
"type": "object",
"properties": {
"alg": {
"$ref": "#/$defs/SignatureAlgorithm"
},
"pem": {
"type": "string"
}
},
"required": [
"pem",
"alg"
]
},
"SignatureAlgorithm": {
"type": "string",
"enum": [
"Ed25519"
]
}
}
}
SignatureVerifyResponse
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SignatureVerifyResponse",
"type": "object",
"properties": {
"result": {
"$ref": "#/$defs/BlisacheActionResult"
}
},
"required": [
"result"
],
"$defs": {
"BlisacheActionResult": {
"type": "string",
"enum": [
"Success",
"Error"
]
}
}
}