Backend API Reference
The Passwordless.dev private API is used by your backend to initiate key registrations, verify sign-ins, retrieve keys for end-users, and more.
All requests made to this API require your API private secret in the header for authentication. Requests made to the public API, which are facilitated by methods in the JavaScript client, will instead require your API public key.
/register/token
Request
POST
requests made to the /register/token
endpoint create a registration token for a user, which will be used by your frontend to negotiate creation of a WebAuth credential.
The request body must include at least a userId
and username
for example:
POST https://v4.passwordless.dev/register/token HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"userId": "107fb578-9559-4540-a0e2-f82ad78852f7",
"username": "[email protected]",
"displayname": "Philip J Fry",
}
const apiUrl = 'https://v4.passwordless.dev';
const payload = {
userId: '107fb578-9559-4540-a0e2-f82ad78852f7', // A WebAuthn User Handle, which should be generated by your application. Max. 64 bytes.
username: '[email protected]', // For display purposes, should help a user identify the user account.
displayname: 'Philip J. Fry', // A human-palatable name for the account.
authenticatorType: 'any', // WebAuthn authenticator attachment modality. Can be "any" (default), "platform" which triggers client device-specific options Windows Hello, FaceID, or TouchID, or "cross-platform", which triggers roaming options like security keys.
userVerification: 'preferred', // Whether the relying party requires locally-invoked authorization for the operation. Can be "preferred" (default), "required", or "optional".
aliases: ['[email protected]'], // An array of user-created identifiers, like emails, which are used to reference a userId.
aliasHashing: true // Whether aliases should be hashed before being stored. Defaults to true.
};
// POST the payload to the Passwordless.dev API using your API private secret.
const { token } = await fetch(apiUrl + '/register', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'ApiSecret': 'myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4',
'Content-Type': 'application/json'
}
}).then((r) => r.json());
The request body may include additional parameters besides those required, all of which are listed here:
Parameter | Description | Example Value |
---|---|---|
userId | Required. A WebAuthn User Handle, which should be generated by your application. This is used to identify your user (could be a database primary key ID or a guid). Max. 64 bytes. Should not contain PII about the user. | "107fb578-9559-4540-a0e2-f82ad78852f7" |
username | Required. A human-palatable identifier for a user account. It is intended only for display, i.e., aiding the user in determining the difference between user accounts with similar displayNames. Used in Browser UI's and never stored on the server | "[email protected]" |
displayname | A human-palatable name for the account, which should be chosen by the user. Used in Browser UI's and never stored on the server. | "Philip J Fry" |
attestation | WebAuthn attestation conveyance preference. Only "none" (default) is supported on 'Trial' & 'Pro' plans. While 'Enterprise' can also use "direct" or "indirect" . (learn more) | "none" (default) |
authenticatorType | WebAuthn authenticator attachment modality. Can be "any" (default), "platform" , which triggers client device-specific options Windows Hello, FaceID, or TouchID, or "cross-platform" , which triggers roaming options like security keys. | "any" (default) |
discoverable | If true , creates a client-side Discoverable Credential that allows sign in without needing a username. | true (default) |
userVerification | Allows choosing preference for requiring User Verification (biometrics, pin code etc) when authenticating Can be "preferred" (default), "required" or "discouraged" . | "preferred" |
expiresAt | Timestamp (UTC) when the registration token should expire. By default, current time + 120 seconds. | "3023-08-01T14:43:03Z" |
aliases | A array of aliases for the userId, such as an email or username. Used to initiate a sign-in on the client side with the signinWithAlias() method. An alias must be unique to the userId. Defaults to an empty array [] . | ["[email protected]"] |
aliasHashing | Whether aliases should be hashed before being stored. Defaults to true . | true |
Response
If successful, the /register/token
endpoint will create a registration token returned as json, for example:
{ "token": "register_wWdDh02ItIvnCKT_02ItIvn..." }
This registration token will will be used by your frontend to negotiate creation of a WebAuth credential.
/signin/verify
Request
POST
requests made to the /signin/verify
endpoint unpack an authentication token, which must be generated by calling a .signinWith*()
method on your frontend (learn more) and included here in the request body, for example:
POST https://v4.passwordless.dev/signin/verify HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"token": "d5vzCkL_GvpS4VYtoT3..."
}
const apiUrl = 'https://v4.passwordless.dev';
// Fetch the authentication token from your frontend.
const payload = { token: req.query.token };
// POST the authentication token to the Passwordless.dev API using your API private secret.
const response = await fetch(apiUrl + '/signin/verify', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'ApiSecret': 'myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4',
'Content-Type': 'application/json'
}
});
The Passwordless.dev private API will unpack the authentication token to check its legitimacy.
Response
If successful, the /signin/verify
endpoint will return a success response object, for example:
{
"success": true,
"userId": "123",
"timestamp": "3023-08-01T14:43:03Z",
"rpid": "localhost",
"origin": "http://localhost:3000",
"device": "Firefox, Windows 10",
"country": "SE",
"nickname": "My Work Phone",
"expiresAt": "3023-08-01T14:43:03Z",
"tokenId": "TODO",
"type": "passkey_signin" // or passkey_register
}
Use the .success
value (true
or false
) to determine next actions, i.e. whether to complete the sign-in (learn more).
/signin/generate-token
Request
POST
requests made to the /signin/generate-token
endpoint create a manually generated authentication token for a user, side-stepping the regular sign-in flow (i.e. the .signinWith*()
methods). The resulting token can then be verified through the /signin/verify
endpoint and used just like a regular authentication token.
POST https://v4.passwordless.dev/signin/generate-token HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"userId": "123",
"timeToLive": 30
}
const apiUrl = 'https://v4.passwordless.dev';
// Generate an authentication token, side-stepping the usual signin process.
const payload = {
userId: '107fb578-9559-4540-a0e2-f82ad78852f7', // ID of the user to generate the token for.
timeToLive: 30 // Time in seconds the token is valid for. Default is 120 seconds.
};
// POST the user ID to the Passwordless.dev API using your API private secret.
const response = await fetch(apiUrl + '/signin/generate-token', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'ApiSecret': 'myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4',
'Content-Type': 'application/json'
}
});
TIP
By default, manually generated authentication tokens are valid for 120 seconds. You can adjust this by setting the timeToLive
parameter in the request body.
Response
If successful, the /signin/generate-token
endpoint will return a response object, for example:
{
"token": "d5vzCkL_GvpS4VYtoT3..."
}
/alias
Request
POST
requests made to the /alias
endpoint add aliases (learn more) to a user, dictated by their userId
, in order to allow sign-in with additional usernames, email addresses, etc.
The request body must include the user's userId
and a complete array of aliases, as pre-existing aliases are overwritten when the POST
request is made, for example:
POST https://v4.passwordless.dev/alias HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"userId": "107fb578-9559-4540-a0e2-f82ad78852f7",
"aliases": [
"[email protected]",
"[email protected]"
],
"hashing": true
}
const apiUrl = 'https://v4.passwordless.dev';
// Fetch an array of existing and new aliases for the user.
const payload = {
userId: '107fb578-9559-4540-a0e2-f82ad78852f7',
aliases: ['[email protected]', '[email protected]'],
hashing: true // Whether to hash aliases before stored, defaults to true.
};
// POST the array to the Passwordless.dev API using your API private secret.
await fetch(apiUrl + '/alias', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'ApiSecret': 'myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4',
'Content-Type': 'application/json'
}
});
TIP
By default, "hashing": true
will be turned on to hash aliases before they're stored. Hashed aliases will not be viewable to you in the admin console.
A few rules to take into consideration when allowing users to create aliases:
- An alias must be unique to the specified
userId
. - An alias must be no more than 250 characters.
- A
userID
may have no more than 10 aliases associated with it.
Response
Alias are never returned in any API responses, and can be hashed to preserve user privacy (see above). If successful, the /alias
endpoint will return an HTTP 204 (No Content) status code.
/credentials/list
Request
GET
requests made to the /credentials/list
endpoint list all registered credentials associated with a user, as dictated by their userId
. The request must include the userId
in question, for example:
GET https://v4.passwordless.dev/credentials/list?userId=107fb578-9559-4540-a0e2-f82ad78852f7 HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
const apiUrl = 'https://v4.passwordless.dev';
// Fetch the userId for the user in question.
const payload = {
userId: '107fb578-9559-4540-a0e2-f82ad78852f7'
};
// POST the userId to the Passwordless.dev API using your API private secret.
const credentials = await fetch(apiUrl + '/credentials/list', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'ApiSecret': 'myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4',
'Content-Type': 'application/json'
}
}).then((r) => r.json());
Response
If successful, the /credentials/list
endpoint will return an array of .json
objects where each object represents a registered credential:
[
{
"descriptor": {
"type": "public-key",
"id": "2mgrJ6LPItfxbnVc2UgFPHowNGKaYBm3Pf4so1bsXSk"
},
"publicKey": "pQECAyYgASFYIPi4M0A+ZFeyOHEC9iMe6dVhFnmOZdgac3MRmfqVpZ0AIlggWZ+l6+5rOGckXAsJ8i+mvPm4YuRQYDTHiJhIauagX4Q=",
"userHandle": "YzhhMzJlNWItNDZkMy00ODA4LWFlMTAtMTZkM2UyNmZmNmY5",
"signatureCounter": 0,
"createdAt": "2023-04-21T13:33:50.0764103",
"aaGuid": "adce0002-35bc-c60a-648b-0b25f1f05503",
"lastUsedAt": "2023-04-21T13:33:50.0764103",
"rpid": "myapp.example.com",
"origin": "https://myapp.example.com",
"country": "US",
"device": "Chrome, Mac OS X 10",
"nickname": "Fred's Macbook Pro 2",
"userId": "c8a32e5b-46d3-4808-ae10-16d3e26ff6f9"
} //, ...
]
Learn more about what these key-value pairs signify.
/credentials/delete
Request
POST
requests made to the /credentials/delete
endpoint delete a specific credential associated with a user, as dictated by a credentialId
. The request must include the credentialId
in question, for example:
POST https://v4.passwordless.dev/credentials/delete HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"credentialId": "qgB2ZetBhi0rIcaQK8_HrLQzXXfwKia46_PNjUC2L_w"
}
Response
If successful, the /credentials/delete
endpoint will return an HTTP 204 (No Content) status code.
/magic-links/send
Request
POST
requests made to the /magic-links/send
endpoint emails the address provided with a Magic Link. This magic link contains a URL, provided by you, that will redirect the recipient to an endpoint in your application. From here, you can send the token Passwordless.dev has embedded in the link to verify the token at signin/verify
.
Important
Passwordless.dev does not store user emails. When integrating Magic Links, you should validate that the email and user id are for the same user. Otherwise, you may introduce a security vulnerability within your application.
The request must include all three fields.
emailAddress
: Recipient of the magic link. Must be a valid email address.urlTemplate
: This is the URL that users will be directed to when they click the link. It should be a valid URL except for the token template string,$TOKEN
. We will swap$TOKEN
with the actual token value before sending the email. In your application, you should parse the token out of the url (most easily done with a query parameter as seen below) and send it to thesignin/verify
endpoint to validate the request.userId
: The identifier of the user the email is intended for.timeToLive
: (OPTIONAL) Number of seconds the magic link token should be valid for. If not set, the default value is 1 hour.
POST https://v4.passwordless.dev/magic-links/send HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"emailAddress": "[email protected]",
"urlTemplate": "https://www.myapp.com?token=$TOKEN",
"userId": "c8a32e5b-46d3-4808-ae10-16d3e26ff6f9",
"timeToLive": 3600
}
Response
If successful, the /magic-links/send
endpoint will return an HTTP 204 (No Content) status code.
If Magic Links has not been enabled, the /magic-links/send
endpoint will return an HTTP 403 (Unauthorized) status code along with a message about enabling the Magic Links feature.
/auth-configs/list
Request
GET
requests made to the /auth-configs/list
endpoint will return a .json
object containing a list of authentication configurations that can be used by the application. It can be filtered to one specific configuration by passing the purpose name as a query parameter.
GET https://v4.passwordless.dev/auth-configs/list HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
GET https://v4.passwordless.dev/auth-configs/list?purpose=step-up HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
For more information about Authentication Configurations, please see the concepts page.
Response
If successful, the /auth-configs/list
endpoint will return a .json
object containing a list of authentication configurations:
{
"configurations": [
{
"purpose": "sign-in",
"timeToLive": 120,
"userVerificationRequirement": "preferred",
"createdBy": "System",
"createdOn": null,
"editedBy": null,
"editedOn": null,
"lastUsedOn": null
},
{
"purpose": "step-up",
"timeToLive": 180,
"userVerificationRequirement": "required",
"createdBy": "System",
"createdOn": null,
"editedBy": null,
"editedOn": null,
"lastUsedOn": null
}
]
}
/auth-configs/add
Request
POST
requests to /auth-configs/add
will create an authentication configuration for the authentication process. There are some restrictions on the request object.
purpose
: A-z, 0-9, -, and _ are the allowed characters. Max length of purpose is 255 characters.timeToLive
: Positive time span value (hh:mm:ss)userVerificationRequirement
:preferred
,required
,discouraged
performedBy
: user identifier to track changes to the configuration
GET https://v4.passwordless.dev/auth-configs/add HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"purpose": "access-secrets-purpose", // identifying string give context to the specific authentication
"timeToLive": "00:03:00", // timespan the token is valid for
"userVerificationRequirement": "preferred", // requirement for if the user has to verify they're allowed to use an authenticator
"performedBy": "user_123" // user identifier to track changes to the configuration
}
Response
If successful, the /auth-configs/add
endpoint will return an HTTP 201 (Created) status code.
If unsuccessful, the /auth-configs/add
endpoint will return an HTTP 400 (Bad Request) status code with a problem details body.
/auth-configs/
Request
POST
requests to /auth-configs
will edit an authentication configuration for the authentication process. There are some restrictions on the request object.
purpose
: Must be an existing purpose (If a new purpose is passed here, a 404 will be returned)timeToLive
: Positive time span value (hh:mm:ss)userVerificationRequirement
:preferred
,required
,discouraged
performedBy
: user identifier to track changes to the configuration
GET https://v4.passwordless.dev/auth-configs HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"purpose": "access-secrets-purpose", // existing purpose
"timeToLive": "00:03:00", // timespan the token is valid for
"userVerificationRequirement": "preferred", // requirement for if the user has to verify they're allowed to use an authenticator
"performedBy": "user_123" // user identifier to track changes to the configuration
}
Response
If successful, the /auth-configs
endpoint will return an HTTP 204 (No Content) status code.
If an unknown purpose is passed through, the /auth-configs
endpoint will return an HTTP 404 (Not Found) status code.
/auth-configs/delete
Request
POST
requests made to the /auth-configs/delete
endpoint delete a specific authentication configuration, as specified by a purpose
.
GET https://v4.passwordless.dev/auth-configs/add HTTP/1.1
ApiSecret: myapplication:secret:11f8dd7733744f2596f2a28544b5fbc4
Content-Type: application/json
{
"purpose": "access-secrets-purpose", // existing purpose
"performedBy": "user_123" // user identifier to track changes to the configuration
}
Response
If successful, the /auth-configs/delete
endpoint will return an HTTP 204 (No Content) status code.
If an unknown purpose is passed through, the /auth-configs/delete
endpoint will return an HTTP 404 (Not Found) status code.
Status codes
The API returns HTTP Status codes for each request.
In case you receive an error, you will also receive a JSON serialized summary of the error in the form of problem details. For more information, see the Errors page.
HTTP Code | Message | Status |
---|---|---|
200 | Everything is OK. | ✅ |
201 | Everything is OK, resource created. | ✅ |
204 | Everything is OK, response is empty. | ✅ |
400 | Bad request. (see problem details for more info). | 🔴 |
401 | You did not identify yourself. | 🔴 |
403 | You are not allowed to perform the action. (see problem details for more info). | 🔴 |
409 | Conflict (see problem details for more info). | 🔴 |
429 | Too many requests (see problem details for more info). | 🔴 |
500 | Something went very wrong on our side. | 🔴 |