For the complete documentation index, see llms.txt. This page is also available as Markdown.

Authentication

This page explains how to authenticate calls to the Verifier API. Every non-public endpoint is protected by an OIDC JWT Bearer token: you obtain a token from your identity provider, send it in the Authorization header, and the Verifier validates it against a JWKS endpoint before serving the request. The only credential the API accepts is a signed, unexpired JWT — there is no client secret or per-verifier shared secret to manage.


Overview

  • The Verifier validates an incoming token's signature against the JSON Web Key Set (JWKS) published by your OIDC provider (for example Keycloak, Auth0, or any compliant issuer). The JWKS location is configured on the service through the OIDC_JWKS_URL environment variable.

  • Authentication is stateless: the service does not maintain login sessions for the API. Each request must carry its own token, and the token's claims identify the caller.

  • Most Verifier endpoints — creating authorization requests, reading verification sessions, and verifying a single credential — require only a valid token, with no specific role. The admin role is enforced separately on the agent-management routes (/agent/*) used to register a verifier and on cloud-wallet routes (/holders/*).

  • For local development, see Development mode.


Sending the Authorization header

Attach the token to every protected request as a Bearer credential:

Authorization: Bearer <your-jwt>

A typical call from a backend looks like this:

const VERIFIER_BASE_URL = 'https://your-verifier.evdi.app';

async function verifyCredential(token: string, credential: string) {
  const res = await fetch(`${VERIFIER_BASE_URL}/credentials/verify`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({ credential }),
  });

  return res.json();
}

The same header applies to the authorization-request and verification-session endpoints. If the header is missing, malformed, or carries an expired token, the request is rejected with 401 Unauthorized before any business logic runs.


Token validation

When a request arrives with a Bearer token, the Verifier performs the following checks:

  • Signature — the token is verified with the RS256 algorithm. The service reads the kid from the JWT header and selects the matching public key from the JWKS published at OIDC_JWKS_URL. A token whose header has no kid, or whose kid does not match any published key, is rejected.

  • Expiration — the exp claim is enforced; expired tokens are rejected.

  • Key caching — fetched signing keys are cached for roughly 10 minutes, and JWKS requests are rate-limited to 10 per minute, so the service does not hit your identity provider on every call.

A request that fails any of these checks receives a 401 Unauthorized response in the standard error shape:

Because validation relies on the JWKS, your OIDC provider can rotate its signing keys without any change to the Verifier — the new key is picked up automatically once the cache expires.


Roles and access control

The Verifier reads roles from the token's claims and applies them per route group:

  • Verifier endpoints require only a valid token. The authorization-request routes (/verifiers/{verifierId}/authorization-requests), the verification-session routes (/verification-sessions/{sessionId}), and the direct verification route (/credentials/verify) carry no role requirement — any caller with a valid JWT may use them.

  • Agent-management endpoints under /agent/* — including POST /agent/verifier, which you call once to register a verifier — additionally require the admin role. The role is read from the token's realm_access.roles array (client roles in resource_access.<client>.roles are also accepted). A valid token without the admin role is rejected with 403 Forbidden.

In practice this means you need an admin-roled token to set up a verifier, but the day-to-day verification calls only need an authenticated token. Restrict who can obtain admin tokens in your identity provider, and scope the tokens your application uses to the minimum it needs.

A typical token payload that satisfies the admin requirement looks like this:


Obtaining a token

The Verifier does not issue tokens; you request them from your OIDC provider. For server-to-server integrations the client-credentials grant is the usual choice — it yields a token tied to a service account rather than an interactive user:

The client_secret above is the credential your application presents to the OIDC provider to obtain a token — it is never sent to the Verifier, which only ever sees the resulting JWT in the Authorization header.

For a One-Click deployment, obtain the token from your realm's Keycloak token endpoint — https://<keycloak-host>/realms/<realm>/protocol/openid-connect/token — using the credentials shown when you created the deployment; see Authenticating to Your Deployment. If you run your own identity provider, use its token endpoint and a client you control (as shown above).


Development mode

To make local testing easier, the service supports a development bypass. Setting AUTH_DISABLED=true skips token validation entirely and treats every request as an authenticated admin user:

With this flag set, you can omit the Authorization header completely and call any endpoint, including the /agent/* routes. When authentication is disabled, OIDC_JWKS_URL is not required. Because AUTH_DISABLED=true removes all access control, use it only for local development and never in any deployed or shared environment.


Best practices

  • Always use HTTPS. Tokens are bearer credentials — anyone who captures one can act as you until it expires — and OpenID4VP requires HTTPS in production regardless.

  • Keep tokens server-side, short-lived, and cached. Obtain and store tokens in your backend (never in browser or mobile clients), and refresh them automatically shortly before exp rather than on every call, so a leaked token has a limited window of usefulness.

  • Scope roles tightly. Reserve the admin role for the one-time verifier setup, and use lower-privilege tokens for ongoing verification traffic.

  • Account for rate limiting. The service applies a global limit of 60 requests per 60 seconds; design your retry and refresh logic with this in mind.


  • Verifier Client Configuration — end-to-end server-side integration, including registering a verifier and creating authorization requests with the Bearer token.

  • Security Considerations — the full security model around transport, request binding, and credential integrity.

Last updated