Quick Start
Integrate passwordless IDToken authentication into your application as a relying party. By the end of this guide, your users will be able to authenticate with their IDToken mobile credential instead of a password.
Prerequisites
Section titled “Prerequisites”- An IDToken operator endpoint (e.g.,
https://auth.example.com) - A registered serviceId (obtained from the IDToken operator during onboarding)
- The tokenId of the user you want to authenticate (provided by your application’s enrollment flow)
1. Register Your Service
Section titled “1. Register Your Service”Before you can initiate authentication, your service must be registered with the IDToken operator. During onboarding, you will receive:
- A serviceId identifying your application
- A set of allowed scopes defining which identity claims your service can request
- Default scopes that apply when no specific scopes are requested
Contact your IDToken operator to register, or see Mutual Service Identity for details on service registration with VDS credentials.
2. Initiate Authentication
Section titled “2. Initiate Authentication”When a user wants to log in, call the /auth/initiate endpoint with their tokenId and the scopes your application needs.
const response = await fetch("https://auth.example.com/auth/initiate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ tokenId: "user-token-id", serviceId: "your-service-id", scopes: ["identity:name", "identity:age_over_18"], }),});
const { sessionId, autoPassword, wsToken, expiresAt } = await response.json();The server returns:
| Field | Description |
|---|---|
sessionId | Unique identifier for this authentication session |
autoPassword | The one-time password to display to the user |
wsToken | Token for connecting to the WebSocket to receive real-time updates |
expiresAt | ISO 8601 timestamp when the OTP expires |
The server simultaneously sends a push notification to the user’s mobile app with the same OTP, the session details, and the scopes you requested.
3. Display the OTP and Listen for Approval
Section titled “3. Display the OTP and Listen for Approval”Display the autoPassword to the user in your browser UI. The user will see the same OTP on their mobile device and confirm it with a biometric check.
Connect to the WebSocket to receive real-time session updates:
const ws = new WebSocket( `wss://auth.example.com/ws/session/${sessionId}?token=${wsToken}`);
ws.addEventListener("message", (event) => { const message = JSON.parse(event.data);
switch (message.type) { case "approved": // Authentication succeeded const { jwt, hash, random } = message; handleLogin(jwt); ws.close(); break;
case "rejected": // User rejected or verification failed showError(message.reason); ws.close(); break; }});When the user approves on their mobile device, the server pushes an approved message containing the signed JWT directly to your WebSocket connection. See WebSocket API for the full message reference.
4. Verify the JWT
Section titled “4. Verify the JWT”The JWT is signed with ES256 (ECDSA P-256). Fetch the operator’s public keys from the JWKS endpoint and verify the signature before trusting any claims.
import * as jose from "jose";
const JWKS_URL = "https://auth.example.com/.well-known/jwks.json";const JWKS = jose.createRemoteJWKSet(new URL(JWKS_URL));
async function verifyIdToken(jwt) { const { payload } = await jose.jwtVerify(jwt, JWKS, { issuer: "https://auth.example.com", audience: "your-expected-audience", }); return payload;}The JWKS endpoint supports HTTP caching (Cache-Control: public, max-age=3600), so your library will efficiently cache the keys.
5. Use the Identity Claims
Section titled “5. Use the Identity Claims”The verified JWT payload contains the identity claims your service requested, scoped to what the user consented to share:
{ "sub": "user-token-id", "iss": "https://auth.example.com", "aud": "your-expected-audience", "iat": 1711929600, "exp": 1711933200, "scope": "identity:name identity:age_over_18", "idtoken": { "tokenId": "user-token-id", "givenName": "Marie", "familyName": "Dupont", "age_over_18": true }}The idtoken object contains only the claims matching the granted scopes. Available scopes include:
| Scope | Claims returned |
|---|---|
identity:name | givenName, familyName |
identity:date_of_birth | dateOfBirth |
identity:nationality | nationality |
identity:age_over_18 | age_over_18 (boolean) |
identity:basic | Composite: identity:name + identity:trust_level |
identity:full | Composite: name, date of birth, nationality, document, trust level |
See Claims & Scopes for the complete scope reference and derived claims.
What Just Happened?
Section titled “What Just Happened?”The full flow follows the ITU-T X.1280 mutual out-of-band authentication protocol:
- Your server requested authentication for a
tokenId - The IDToken server generated a one-time password and pushed it to both the browser (via your WebSocket) and the user’s mobile app (via push notification)
- The user confirmed the OTP matched and approved with a biometric check on their mobile device
- The mobile app cryptographically signed the approval and sent it to the IDToken server
- The server verified the signature, issued a JWT with the consented claims, and delivered it to your WebSocket
No password was transmitted. No user database was queried. Identity was proven cryptographically from the VDS credential on the user’s device.
Next Steps
Section titled “Next Steps”- Integration Guide — Full integration options including OIDC and SAML
- Selective Disclosure — All available scopes and derived claims
- Authentication Flow — Protocol-level walkthrough of each phase
- WebSocket API — Complete WebSocket event reference
- REST API — Full endpoint documentation
- Security Model — Threat model and cryptographic design