As independent second-factor
You can use Visual Passcodes to add a strong visual second factor of authentication to an existing login flow where the user has already been authenticated with a primary factor (e.g., username and password).
General Description
This flow is initiated by including the custom scope 2fa in the authorization request. The Visual Passcodes authentication process then acts as the second factor. In this flow, the server will only return an id_token.
Integration Steps
- First Factor Authentication: First, authenticate the user with your existing primary method (e.g., username/password). This establishes the user's initial identity.
- Construct 2FA Authorization URL: After the first factor is successful, redirect the user's browser to the Visual Passcodes Authorization Endpoint. The request must include:
scope:openid 2fa.login_hint: The username or unique identifier of the user who just completed the first factor. This is strongly recommended as it pre-populates Visual Passcodes' context for the authentication challenge.- All other required OIDC parameters (
client_id,redirect_uri,state,nonce, and PKCE parameters even if it's a confidential client).
- Handle the Visual Passcodes Callback: The user completes the Visual Auth challenge at Visual Passcodes and is redirected back to your
redirect_uriwith anauthorization_code. - Exchange Code for ID Token: Your application's backend exchanges this
authorization_codeat the Visual Passcodes Token Endpoint, just as in a regular flow. - Receive ID Token (Only): Visual Passcodes will validate the request and respond with a JSON object containing only an
id_token. Confirm thataccess_tokenandrefresh_tokenare absent. - Validate ID Token: Your backend must validate the
id_token's signature using Visual Passcodes' public key from the JWKS Endpoint. You must also validate thenonceto prevent replay attacks. - Complete Login: Once the
id_tokenis successfully validated, your application can consider the user fully authenticated with 2FA and proceed with establishing their final session within your system.
Prerequisites
- Client Registration: The 3rd party developer must register their application with the OpenID Connect provider. This involves providing application details (name, description, etc.) and specifying a redirect URI. The server will issue a
client_idand aclient_secret. - PKCE Implementation: The 3rd party application must implement PKCE.
- User already authenticated: We assume that the user has been already authenticated with a first factor (e.g., username + Password). That way, the identity of the user (e.g., username) is already known and the second factor is based on this identity.
Authorization Code Flow with PKCE
Authorization Request with 2fa Scope
The 3rd party client application (belonging to the external party) initiates the second factor authorization code flow by redirecting the user to Image-based authentication Identify Provider (Visual Passcodes) server's authorization endpoint. In this request, the scope parameter includes the custom 2fa scope, in addition to other standard scopes like openid. For example:
GET /oidc/authorize/{tenant_id}?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope=openid%202fa&code_challenge={code_challenge}&code_challenge_method=S256&nonce={nonce}&state={state}&login_hint={login_hint}&email_hint={login_hint}
The endpoint call also includes the user identifier (username) of the user that needs to be authenticated with the image.
Processing by the Authorization Server
Upon receiving the authorization request with the 2fa scope, Visual Passcodes server's authorization server recognizes that 2FA is required for the external party's user. The server then performs the following steps:
- Authentication (Treated as 2FA): In this specific implementation, the standard user authentication process at Visual Passcodes server is treated as the de facto second factor of authentication for the external party. The server initiates the authentication process, forcing the external party's user to log in using credentials recognized by this server. This login process serves as the 2FA step.
- 2FA Challenge: Since the primary authentication is the 2FA, there is no separate 2FA challenge step in this flow.
- 2FA Verification: The user provides their login credentials. This server verifies these credentials as part of its standard authentication process, effectively fulfilling the 2FA requirement.
- Token Issuance:
- If the authentication is successful, this server proceeds to issue an ID token.
- Critically, in this specific 2FA flow, this server is configured to only return an ID token. This server omits the refresh token and the access token in the response. This is a key characteristic of this 2FA implementation.
Token Response
This server sends the ID token back to the client application (belonging to the external party) via the redirect URI, as defined in the authorization code flow.
HTTP/1.1 302 Found
Location: {redirect_uri}?code={authorization_code}&state={state}
{
"id_token": "{id_token_value}",
"token_type": "Bearer",
"expires_in": 3600
}
- Important: Note the absence of
refresh_tokenandaccess_tokenin the response.
Client Application Handling
The client application (of the external party) receives the ID token.
- The client application uses the ID token to obtain information about the user, and to confirm that the user has successfully authenticated with 2FA.
- Since no refresh token is provided, the client application will need to re-authenticate the user (and they will have to go through the 2FA process again with this server) once the ID token expires.
- It is strongly recommended that the client application perform thorough validation of the response received from this server in its backend. This is crucial for maintaining the security of the authentication process.
- Specifically, the client application should validate the nonce parameter present in the ID token against the nonce that was included in the original authorization request. This validation is essential to mitigate replay attacks, where an attacker might attempt to use a previously obtained, valid ID token.
- The client application should validate the ID token signature using the server public key. The server uses EdDSA to sign the tokens.
Endpoint Implementation Details
To implement this 2FA flow, the external party's application needs to implement two key endpoints:
Authorization Request Endpoint
- This endpoint is responsible for initiating the OpenID Connect authorization code flow with this server.
- The external party's backend (acting as a confidential client) constructs the authorization URL, including the necessary parameters:
response_type: Set to code.client_id: The client ID of the external party's application, as registered with this server.redirect_uri: A URI under the control of the external party's application, where this server will redirect the user after authentication.scope: Must include openid and the custom2fascope (e.g.,openid 2fa).code_challengeandcode_challenge_method: To enable PKCE (Proof Key for Code Exchange), the client generates acode_verifier, transforms it into acode_challenge, and includes thecode_challengeandcode_challenge_methodset to S256 in the request.nonce: Include a unique value to mitigate replay attacks.state: Include a unique value to maintain state between the request and callback.login_hint: Strongly recommended: Include the username of the user being authenticated. This binds the authentication request to a specific user.
- The external party's backend then redirects the user's browser to this constructed URL at this server's authorization endpoint.
Callback Endpoint
- This endpoint is the
redirect_urispecified in the authorization request. This server redirects the user's browser to this endpoint after the user has authenticated (and authorized the application). - This server includes the authorization
codeand thestatein the query parameters of the redirect URL. - The external party's backend at this endpoint:
- Retrieves the
codeand the state from the query parameters. - Important: Verify that the state parameter matches the state parameter from the original authorization request. This is crucial for preventing CSRF attacks.
- Sends a POST request to this server's token endpoint to exchange the code for an ID token. This request MUST include:
grant_type: Set toauthorization_code.code: The authorization code received from this server.redirect_uri: The sameredirect_uriused in the authorization request.client_id: The client ID of the external party's application.client_secret: The client secret for the external party's application (this is why this exchange must happen on the backend, not in the user's browser).code_verifier: The value generated in the Authorization Request Endpoint.
- This server's token endpoint validates the request (including the
code_verifierif PKCE was used) and, upon successful validation, returns an ID token. - The external party's backend then validates the ID token:
- Verifies the signature of the ID token.
- Validates the claims in the ID token, including issuer, audience, expiration, and nonce. The nonce value MUST match the one sent in the original authorization request.
- If the ID token is valid, the user is considered successfully authenticated with 2FA.
- Retrieves the
State Parameter Usage
- The state parameter is crucial for security and data integrity in the authorization code flow. It allows the external party's application to:
- Prevent CSRF Attacks: By including a unique, unpredictable value in the authorization request and verifying it in the callback, the application can ensure that the user is not being tricked into authorizing an unintended request.
- Correlate Request and Response: The state parameter links the authorization request with the corresponding callback, ensuring that the received authorization code is associated with the original request.
- In the context of this 2FA implementation, the external party's application should use the state parameter to bind it with other session-specific data, such as:
- The
noncevalue. - The
code_verifiervalue. - Any other data relevant to the external party's 2FA handling.
- The
- This binding can be achieved by:
- Creating a server-side session or storage associated with the state value.
- Storing the
nonce,code_verifier, and other data in this session or storage, indexed by the state value.
- Upon receiving the callback from this server, the external party's application retrieves the data associated with the state parameter and uses it to validate the response and proceed with the 2FA flow.