Token Verification
At the end of a registration or authentication session, LoginID provides a LoginID token. If your backend uses this token, it is important to ensure that it has been properly signed by LoginID. You can verify the token in one of the following ways:
Verifying With /.well-known/jwks.json Endpoint
LoginID tokens are compatible with OAuth 2.0 and OpenID Connect (OIDC) standards. While our service does not fully implement OIDC, this endpoint follows OIDC and OAuth 2.0 conventions to maintain compatibility.
Each LoginID application has a dedicated JWKS (JSON Web Key Set) endpoint, found at {LOGINID_BASE_URL}/fido2/v2/.well-known/jwks.json
. This endpoint provides the JWKS required to verify the LoginID tokens associated with your application.
Consult the OpenAPI reference for technical details.
- Nodejs
This is a backend NodeJS example showing how to verify the LoginID token using the JWKS endpoint.
Here are the key steps:
- Decode the JWT token to get the key ID (kid).
- Check if the corresponding key is cached.
- If not cached, fetch the key set (JWKS) from JWKS endpoint.
- Convert the key to PEM format and cache it.
- Verify the JWT token using the key.
import { default as fetch } from "node-fetch";
import { default as jwt } from "jsonwebtoken";
import jwkToPem from "jwk-to-pem";
const baseUrl = process.env.LOGINID_BASE_URL; // The base URL of your LoginID service
const jwksUri = `${baseUrl}/fido2/v2/.well-known/jwks.json`;
// Simulate a storage mechanism for persisting transaction data
const cache = new Map();
const verifyTokenWithKey = (token, verificationKey) => {
try {
const decoded = jwt.verify(token, verificationKey);
console.log("Token is valid:", decoded);
return decoded;
} catch (err) {
throw new Error(`Token verification failed: ${err.message}`);
}
};
export async function verfiyLoginIDToken(jwtToken, expectedUsername) {
const decodedJwt = jwt.decode(jwtToken, { complete: true });
if (!decodedJwt) {
throw new Error("Failed to decode JWT token");
}
const kid = decodedJwt.header?.kid;
const issuer = decodedJwt.payload?.iss;
const username = decodedJwt.payload?.username;
// Ensure the issuer matches the base URL of the loginid service
if (issuer !== baseUrl) {
throw new Error(`Issuer mismatch: expected ${baseUrl}, but got ${issuer}`);
}
// Check if the username matches the expected value
if (username !== expectedUsername) {
throw new Error(
`Username mismatch: expected ${expectedUsername}, but got ${username}`
);
}
// Check if the key is in the cache
if (cache.has(kid)) {
return verifyTokenWithKey(jwtToken, cache.get(kid));
}
try {
// Fetch the JWKS if the key is not cached
const response = await fetch(jwksUri);
if (!response.ok) {
throw new Error("Failed to fetch JWKS");
}
const jwks = await response.json();
const key = jwks.keys.find((k) => k.kid === kid);
if (!key) {
throw new Error("Unable to find a matching key");
}
// Convert the JWK to PEM format using jwk-to-pem
const verificationKey = jwkToPem(key);
// Cache the PEM key
cache.set(kid, verificationKey);
// Verify the token with the cached key
return verifyTokenWithKey(jwtToken, verificationKey);
} catch (error) {
throw new Error(`Token validation error: ${error.message}`);
}
}
Verifying With Token Verify Endpoint
LoginID offers an API to verify JWT authenticity. A 204 response confirms a valid token, ensuring the trustworthiness of its claims and data. To verify a session token, you'll need an API key from your dashboard, which will be used as the username for Basic Authentication. Learn more here.
It's important to include a claim check, such as verifying the username. LoginID will validate the signature, expiry, and issuer (base URL).
- Curl
- Nodejs
Here is a sample validation using CURL
curl -X POST "{APP_BASE_URL}/fido2/v2/mgmt/token/verify" \
-H "Content-Type: application/json" \
-u $APP_API_KEY: # Will set authorization header to basic.
-d '{
"jwtAccess": "{RESULT_JWT}"
}'
In this example we are demonstrating how to validate LoginID session token using Nodejs.
import fetch from "node-fetch";
import jwt from "jsonwebtoken";
const LOGINID_BASE_URL = process.env.LOGINID_BASE_URL || "";
const LOGINID_API_KEY = process.env.LOGINID_API_KEY || "";
export const verifyLoginIDToken = async (token, expectedUsername) => {
const decodedToken = jwt.decode(token);
// Extract the username from the payload
const username = decodedToken.username;
// Check if the username matches the expected value
if (username !== expectedUsername) {
throw new Error(
`Username mismatch: expected ${expectedUsername}, but got ${username}`
);
}
// Proceed with verifying the token with LoginID
const basicToken = Buffer.from(`${LOGINID_API_KEY}:`).toString("base64");
const res = await fetch(`${LOGINID_BASE_URL}/fido2/v2/mgmt/token/verify`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${basicToken}`,
},
body: JSON.stringify({ jwtAccess: token }),
});
// Check if the external service responded with 204 No Content (success)
if (res.status !== 204) {
const data = await res.json();
throw new Error(data.message || data.msg);
}
};
Ensure the Authorization header format is correct. It should include the character :
and an empty password.
For verification details, consult the OpenAPI reference.