Skip to main content

Verification and Authentication Flow

The ISBE Credential IDP is a federated Identity Provider (IDP) that allows any OpenID Connect-compatible system to delegate the authentication phase to a process based on verifiable credential presentation. The IDP acts as an intermediary between Keycloak and ISBE's verification service.

What the Credential IDP does

Instead of asking for a username and password, the IDP shows the user a QR code which they scan with their digital wallet. The wallet sends the verifiable credential (VC) to ISBE's verification service, which validates it cryptographically and notifies the IDP. If validation is correct, the IDP generates an OIDC token and delivers it to Keycloak, which completes the authentication process and redirects the user to the ISBE portal with the session started.

Architecture

User (browser)
│ 1. Accesses ISBE portal

ISBE Portal ──► Keycloak (delegates authentication)
│ 2. Redirects to credential IDP

Credential IDP ──► shows QR to user
│ │
│ polling │ 3. User scans QR
│ ▼
│ User's digital wallet
│ │
│ │ 4. Presents VC
│ ▼
│ Verification service
│ │ 5. Validates VC cryptographically
│ │ (verifies signature against issuer's DID)
│ │ 6. Notifies IDP
◄──────────────┘
│ 7. IDP generates OIDC tokens

Keycloak (receives token)
│ 8. Completes authentication

ISBE Portal (session started)

Step-by-step flow

1. The user accesses the ISBE portal

The user navigates to the ISBE portal and clicks "Sign in". The portal redirects to Keycloak to manage authentication.

2. Keycloak redirects to the Credential IDP

The user chooses to authenticate via verifiable credential. Keycloak redirects to the IDP's authorization endpoint (/accounts/authorize) with standard OAuth2 parameters (including the scope representing the credential type the user must present).

3. The IDP shows the QR

The IDP generates a verification session and renders an HTML page with:

  • A QR code that the user must scan with their wallet.
  • An informational text field with the scope (credential type) requested from the user.
  • A polling mechanism (the browser periodically queries GET /idp/api/auth/requests with the session code to know if verification has been completed).

4. The user scans the QR with their wallet

The wallet reads the credential offer URL encoded in the QR. This URL follows the OID4VP (OpenID for Verifiable Presentations) protocol.

5. The wallet presents the VC

The wallet selects the appropriate VC according to the requested type (OID_CREDENTIAL, default IsbePortalLearCredential) and sends it signed. The verification service checks:

  • That the VC's JWT signature is valid for the key of the issuer's assertionMethod according to the DID Document.
  • That the issuer is registered in the TIR with the accreditation corresponding to the VC type.
  • That the VC has not expired and has not been revoked.

6. Verification completes

Once the credential is verified, the IDP receives the notification with the claims:

The IDP saves the claims in the cache with key session_code and marks the session as SUCCESS.

7. The browser detects success (polling)

The user's browser, which was polling GET /idp/api/auth/requests?session=<code>, receives the response {"status": "SUCCESS"} and automatically redirects to the OAuth2 flow.

8. The IDP applies mappers and generates OIDC tokens

The IDP transforms the claims from the presented VC into OIDC token attributes according to the mapper configuration of the active scope. For example, for the scope openid-vp-lear:

username          = credentialSubject.mandate.mandatee.email
given_name = credentialSubject.mandate.mandatee.firstName
name = concat(firstName, ' ', lastName)
family_name = credentialSubject.mandate.mandatee.lastName
preferred_username= credentialSubject.mandate.mandatee.email
organization = credentialSubject.mandate.mandator.organization
organization_identifier = credentialSubject.mandate.mandator.organizationIdentifier
user_identifier = credentialSubject.mandate.mandatee.employeeId
email = credentialSubject.mandate.mandatee.email
power = str(credentialSubject.mandate.power)

The IDP generates the id_token, access_token, and refresh_token signed with its RSA private key (IDP_OIDC_PRIVATE_KEY).

Only RSA is supported for IDP token signing

The IDP signs OIDC tokens exclusively with RSA keys, due to a limitation of the AllAuth library. It is not possible to use EC keys (P-256, secp256k1) to sign IDP tokens in the current version.

9. Keycloak completes authentication

Keycloak receives the id_token from the IDP, maps the attributes to its own claims, and returns the user to the ISBE portal with the session started.

Scopes and mappers

The Credential IDP is designed to be configurable per environment. The IDP administrator can create multiple scopes, each representing a different presentation type.

A scope has:

  • Scope: Identifier for the type of presentation requested from the user.
  • Name: Descriptive internal name.
  • Consent Text: Text shown to the user when asked to present the credential.
  • Mappers: Claim transformation rules.

Mappers support three operations:

OperationSyntaxExample
Direct attribute copykey = path.to.fieldemail = mandatee.email
Concatenationkey = concat(v1, sep, v2)name = concat(mandatee.firstName, ' ', mandatee.lastName)
JSON serializationkey = str(field)power = str(power)
Limitation: only 1 credential per presentation session is supported

The IDP currently only correctly processes presentations of a single credential. Do not present more than one credential per authentication session.

OIDC clients registered in the IDP

Each system that wants to use the credential IDP (currently Keycloak, acting as broker) must be registered as an OIDC client in the IDP admin panel. The minimum configuration for a client includes:

  • Client ID: Client identifier.
  • Scopes: List of allowed scopes (e.g. openid, profile, email, openid-vp-lear).
  • Redirect URIs: The Keycloak return URL (https://<keycloak-domain>/broker/<idp-alias>/endpoint).
  • CORS Allowed Origins: Keycloak domain.

IDP OIDC endpoints

The IDP exposes the standard OpenID Connect endpoints:

EndpointDescription
GET /.well-known/openid-configurationOIDC discovery document with the list of endpoints and supported scopes.
GET /accounts/authorizeOAuth2 authorization endpoint. Redirects the user to the QR presentation screen.
POST /accounts/tokenExchange of authorization code for access_token, id_token, and refresh_token.
GET /accounts/userinfoReturns the authenticated user's claims according to the requested scopes.
GET /idp/api/auth/requestsPolling of verification session state (PENDING / SUCCESS / FAILED).