User tokens and JWKS
The signed ES256 token a successful enroll or match returns, and how to verify it.
Every successful POST /v1/enroll and POST /v1/match returns a user token and its expiry. The token is an ES256 JWT that lasts one hour.
Claims
| Claim | Value |
|---|---|
iss | https://api.facecard.dev |
aud | facecard |
sub | The FaceCard user id |
app_id | The app the user belongs to |
email | The user's email |
iat, exp | Issued at and expiry, in seconds since the epoch |
jti | A unique token id |
What the token is for
The token proves FaceCard matched a face in your app. You use it in two ways:
- Send it to your server and verify it, then start your own session. This is the normal flow.
- Call
/v1/meroutes withAuthorization: Bearer <token>so the user can read or manage their own profile. See API reference: me.
Do not accept a token that your server has not verified. A token that arrived from the browser is just a string until you check it.
Verify online
POST /v1/tokens/verify with your secret key returns { "valid": true, "claims": {...} } or { "valid": false, "reason": "..." }. See API reference: tokens.
Verify offline with JWKS
The public signing keys are published at:
https://api.facecard.dev/.well-known/jwks.jsonAny JWT library can verify the token against that key set:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://api.facecard.dev/.well-known/jwks.json'));
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://api.facecard.dev',
audience: 'facecard',
});
// Also check payload.app_id equals your app id.Details to respect:
- Choose the key by the token's
kid. The signing key rotates, and during a rotation the set holds both the current and the previous public key. - Cache the JWKS (the endpoint sends a short cache lifetime) and refetch when you see an unknown
kid. - Check
app_id. A token from another FaceCard app is a valid FaceCard token but not a user of your app. - Offline verification cannot know that a user was deleted or signed out after the token was issued. A token revoked with
POST /v1/me/logoutstill passes the JWKS check until it expires.POST /v1/tokens/verify(tokens.verifyin the SDK) is the revocation aware check. For sensitive actions use it, or keep your own session short.
The SDK does this for you with verifyTokenOffline(token). See server SDK.
Signing out
POST /v1/me/logout puts the token's jti on a revocation list until the token expires. The API then rejects it with 401 token_revoked, and POST /v1/tokens/verify returns { "valid": false, "reason": "revoked" }. See /v1/me routes.
Cookie sessions
First party apps (the FaceCard consumer app itself) also receive an HttpOnly fc_session cookie. Third party apps get the token in the response body only.