OpenID Connect
IDToken acts as an OpenID Connect (OIDC) Provider, enabling any OIDC-compliant application to accept IDToken authentication using standard protocols. This provides integration with cloud services (Google Workspace, Microsoft 365, etc.), SaaS platforms, and modern web applications without custom integration code.
Why OIDC?
Section titled “Why OIDC?”IDToken already issues signed JWTs with identity claims — OIDC formalizes this into a standard protocol that any application can consume. While the direct JWT integration works well for custom applications, OIDC enables:
- Cloud service SSO — Google Workspace, Microsoft 365, Slack, GitHub, and thousands of SaaS applications support OIDC out of the box
- Standard discovery — applications auto-configure by fetching
/.well-known/openid-configuration - Interoperability — any OIDC client library (in any language) works without custom code
- Compliance — OIDC is a recognized standard for identity federation in enterprise and government contexts
Architecture
Section titled “Architecture”The OIDC layer sits on top of the existing X.1280 authentication flow, just as the SAML IdP does. The same OTP + mobile face verification produces an OIDC id_token instead of (or in addition to) a JWT or SAML assertion.
OIDC Endpoints
Section titled “OIDC Endpoints”| Endpoint | Method | Description |
|---|---|---|
/.well-known/openid-configuration | GET | OIDC discovery document |
/oidc/authorize | GET | Authorization endpoint (starts auth flow) |
/oidc/token | POST | Token endpoint (exchanges code for tokens) |
/oidc/userinfo | GET | UserInfo endpoint (returns identity claims) |
/.well-known/jwks.json | GET | JSON Web Key Set (existing endpoint) |
/oidc/end-session | GET/POST | RP-initiated logout |
Discovery Document
Section titled “Discovery Document”The OIDC discovery document at /.well-known/openid-configuration advertises the provider’s capabilities:
{ "issuer": "https://auth.idtoken.id3.eu", "authorization_endpoint": "https://auth.idtoken.id3.eu/oidc/authorize", "token_endpoint": "https://auth.idtoken.id3.eu/oidc/token", "userinfo_endpoint": "https://auth.idtoken.id3.eu/oidc/userinfo", "jwks_uri": "https://auth.idtoken.id3.eu/.well-known/jwks.json", "end_session_endpoint": "https://auth.idtoken.id3.eu/oidc/end-session", "scopes_supported": [ "openid", "profile", "email", "identity:name", "identity:date_of_birth", "identity:nationality", "identity:age_over_18", "identity:age_over_21", "identity:trust_level" ], "response_types_supported": ["code"], "grant_types_supported": ["authorization_code"], "subject_types_supported": ["public", "pairwise"], "id_token_signing_alg_values_supported": ["ES256"], "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post" ], "claims_supported": [ "sub", "name", "given_name", "family_name", "birthdate", "nationality", "age_over_18", "age_over_21", "trust_level" ]}Authorization Flow
Section titled “Authorization Flow”IDToken uses the Authorization Code Flow (the most secure OIDC flow):
- The client redirects the user to
/oidc/authorizewith the requested scopes - IDToken displays the login page (enter tokenId)
- The standard X.1280 authentication flow runs (OTP + mobile face verification)
- On success, IDToken redirects back to the client with an authorization code
- The client exchanges the code for an
id_tokenandaccess_tokenvia the/oidc/tokenendpoint - Optionally, the client calls
/oidc/userinfofor additional claims
Authorization Request
Section titled “Authorization Request”GET /oidc/authorize? response_type=code &client_id=bar-le-central &redirect_uri=https://bar-le-central.fr/callback &scope=openid identity:name identity:age_over_18 &state=random-csrf-token &nonce=random-replay-protectionToken Response
Section titled “Token Response”{ "access_token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 3600, "id_token": "eyJhbGciOiJFUzI1NiIs...", "scope": "openid identity:name identity:age_over_18"}ID Token Claims
Section titled “ID Token Claims”The id_token is a JWT (ES256-signed) containing identity claims mapped to OIDC standard claim names where possible:
| IDToken Scope | OIDC Claim | Type | Description |
|---|---|---|---|
openid | sub | string | Subject identifier (tokenId) |
identity:name | given_name, family_name, name | string | Full name from VDS |
identity:date_of_birth | birthdate | string | ISO 8601 date |
identity:nationality | nationality | string | 3-letter ISO code (custom claim) |
identity:age_over_18 | age_over_18 | boolean | Derived from birthdate (custom claim) |
identity:age_over_21 | age_over_21 | boolean | Derived from birthdate (custom claim) |
identity:trust_level | trust_level | number | 1 (self), 2 (operator), 3 (eIDAS) |
identity:photo | picture | string | Base64-encoded portrait (restricted) |
Example ID Token Payload
Section titled “Example ID Token Payload”{ "iss": "https://auth.idtoken.id3.eu", "sub": "vds-token-uuid", "aud": "bar-le-central", "exp": 1705316400, "iat": 1705312800, "nonce": "random-replay-protection", "given_name": "Jean", "family_name": "Dupont", "name": "Jean Dupont", "age_over_18": true, "trust_level": 3, "acr": "urn:idtoken:acr:face-oob-high"}Standard vs. Custom Claims
Section titled “Standard vs. Custom Claims”Where possible, IDToken maps to OIDC standard claims. Claims without a standard equivalent use IDToken-specific names:
| Category | Standard Claims Used | Custom Claims |
|---|---|---|
| Name | given_name, family_name, name | — |
| Birth | birthdate | — |
| Photo | picture | — |
| Nationality | — | nationality |
| Age verification | — | age_over_18, age_over_21, age_range |
| Trust | — | trust_level |
| Identity | — | is_eu_citizen, name_initial |
Subject Identifier Types
Section titled “Subject Identifier Types”| Type | Behavior | Use Case |
|---|---|---|
public | sub = tokenId (same across all clients) | Default; enables cross-service identity linking |
pairwise | sub = HMAC(tokenId, client_id) | Privacy-preserving; different sub per client, preventing cross-service tracking |
The subject type is configured per client registration.
Client Registration
Section titled “Client Registration”OIDC clients (Relying Parties) are registered with the IDToken operator. Each client record defines:
| Field | Description |
|---|---|
client_id | Unique identifier for the client application |
client_secret | Secret for token endpoint authentication |
redirect_uris | Allowed callback URLs (strict matching) |
allowed_scopes | Maximum scopes this client can request |
subject_type | public or pairwise |
id_token_signed_response_alg | ES256 (default, ECDSA P-256) |
token_endpoint_auth_method | client_secret_basic or client_secret_post |
Selective Disclosure
Section titled “Selective Disclosure”OIDC scopes map directly to the IDToken selective disclosure system:
- The
openidscope is always required (returnssub) - Identity scopes (
identity:name,identity:age_over_18, etc.) control which claims appear in theid_tokenand/oidc/userinforesponse - The mobile app shows the consent screen with the requested scopes
- The user can deny individual scopes — the
id_tokenwill only contain approved claims - Granted scopes are cryptographically bound to the user’s ECDSA signature
OIDC Scope Mapping
Section titled “OIDC Scope Mapping”| OIDC Standard Scope | Maps To | Claims Returned |
|---|---|---|
openid | — | sub |
profile | identity:name | given_name, family_name, name |
email | — | Not available (VDS contains no email) |
For identity claims beyond standard OIDC scopes, use IDToken’s native scopes (identity:age_over_18, identity:nationality, etc.) alongside openid.
Authentication Context
Section titled “Authentication Context”The acr (Authentication Context Class Reference) claim in the id_token reflects the trust level:
| Trust Level | ACR Value |
|---|---|
| 1 (self-issued) | urn:idtoken:acr:face-oob-low |
| 2 (operator-verified) | urn:idtoken:acr:face-oob-substantial |
| 3 (NFC + eIDAS) | urn:idtoken:acr:face-oob-high |
Clients can request a minimum ACR via the acr_values parameter in the authorization request.
UserInfo Endpoint
Section titled “UserInfo Endpoint”The /oidc/userinfo endpoint returns identity claims for the authenticated user:
GET /oidc/userinfoAuthorization: Bearer {access_token}{ "sub": "vds-token-uuid", "given_name": "Jean", "family_name": "Dupont", "name": "Jean Dupont", "birthdate": "1985-06-15", "nationality": "FRA", "age_over_18": true, "trust_level": 3}The response contains only claims matching the scopes granted during authentication.
Comparison with SAML
Section titled “Comparison with SAML”| Feature | OIDC | SAML |
|---|---|---|
| Token format | JWT (compact, JSON) | XML assertion (verbose) |
| Transport | JSON over HTTPS | XML over HTTP POST |
| Discovery | /.well-known/openid-configuration | IdP metadata XML |
| Best for | Modern web/mobile apps, cloud services | Legacy enterprise systems |
| Signing | ES256 (ECDSA P-256) | RSA-SHA256 or ECDSA-SHA256 |
| Selective disclosure | Native (via scopes) | Via attribute mapping |
Both OIDC and SAML use the same underlying X.1280 authentication flow. The choice depends on what the relying party supports.