Skip to main content
A custom authentication flow can return an OwnID access-token (JWT) to your application in the accessToken field. Before performing a sensitive action, verify the OwnID access-token on your server, confirm that it was issued for your OwnID application, and check that it contains the authentication claim your action requires. The following TypeScript example uses jose to verify the RS256 signature on the OwnID access-token with OwnID’s JSON Web Key Set (JWKS). It also validates standard token claims, enforces one-time use, checks the completed authentication methods, and extracts the user’s login identifier. For a single copyable implementation, jump to the complete example. Install jose before using the example:

Configure the issuer and signing keys

Set your OwnID application ID and environment. The issuer identifies the OwnID application that is allowed to mint an OwnID access-token for your server. Use the same value as the expected audience so that a valid token intended for another application is rejected.
createRemoteJWKSet selects the public key whose kid matches the OwnID access-token header. The library caches fetched keys and refreshes the JWKS when it encounters an unknown key ID, allowing normal key rotation without hard-coding public keys.

Model the authentication requirements

The authorization_details claim records which authentication or verification methods the user completed. RequireAuth lets each custom action require one method, at least one method from a set, or every method in a set.
Treat these values as authorization evidence only after verifying the OwnID access-token (JWT). Reading claims from an unverified token does not prove that OwnID produced them.

Prevent OwnID access-token replay

The JWT ID (jti) uniquely identifies an OwnID access-token. Store consumed IDs until their tokens expire so that a captured token cannot authorize the same action again.
The placeholder above must be backed by shared, durable storage in production. Claiming a jti should be atomic—for example, with Redis SET NX and an expiry or a database uniqueness constraint—so two concurrent requests cannot both consume the same token. Never use process-local memory when your service runs more than one instance.

Verify and consume the OwnID access-token

jwtVerify verifies the RS256 signature and validates exp, iss, and aud. The function rejects malformed, expired, incorrectly issued, or incorrectly targeted tokens. It then requires both jti and exp, evaluates the action’s authentication requirement, and atomically records the OwnID access-token as consumed. A token whose ID was already stored is rejected.
A null result is intentionally nonspecific. Return a generic unauthorized response to the client rather than exposing whether a signature, claim, or replay check failed. Log suitable diagnostic details only in your protected server logs.

Check the authorization details

Convert the claim array to a set and evaluate the requested policy:
  • A string requires that exact method.
  • oneOf succeeds when the OwnID access-token contains at least one listed method.
  • allOf succeeds only when the OwnID access-token contains every listed method.
  • Omitting requiredAuth accepts any otherwise valid token. For sensitive actions, pass an explicit requirement.

Extract the login identifier

OwnID formats the subject (sub) as <IdentifierType>:<value>. Split it only once conceptually: a missing type, missing value, or extra colon makes the value invalid.
Check the returned identifier type before using its value. For example, an action that updates an email-based account should reject a phone number or username subject.

Authorize a custom action

Call the verifier in the server endpoint that performs the action. Read the accessToken value from the custom-action request, then verify and consume that OwnID access-token before applying the action. JWKS resolution can require a network request, so always await it.
Only execute the custom action after every check succeeds. Adapt the required authentication methods and accepted identifier types to the risk and data model of each action.

Complete example

The full implementation below combines the sections above into one file: