Verify tokens

Your application receives an access token. You verify it before you trust it. You have two ways.

Verify offline with the JWKS

This is the fast path. You fetch the public keys once and cache them. You verify every token locally. Authly never sees the check.

Authly signs tokens with RS256. It publishes the public keys here.

https://api.authly.example/.well-known/jwks.json

Check four things on every token.

  1. The signature matches a published key.
  2. The iss claim equals the Authly issuer.
  3. The aud claim equals your client_id.
  4. The exp claim is in the future.

Node

import { createRemoteJWKSet, jwtVerify } from "jose";

const jwks = createRemoteJWKSet(
  new URL("https://api.authly.example/.well-known/jwks.json"),
);

export async function verify(token) {
  const { payload } = await jwtVerify(token, jwks, {
    issuer: "https://api.authly.example",
    audience: "your_client_id",
  });
  return payload.sub;
}

Ruby

require "jwt"
require "net/http"

jwks = JSON.parse(Net::HTTP.get(URI("https://api.authly.example/.well-known/jwks.json")))
jwk_loader = ->(options) { JWT::JWK::Set.new(jwks) }

payload, = JWT.decode(token, nil, true, {
  algorithms: ["RS256"],
  iss: "https://api.authly.example",
  verify_iss: true,
  aud: "your_client_id",
  verify_aud: true,
  jwks: jwk_loader,
})

PHP

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

$jwks = json_decode(file_get_contents(
  "https://api.authly.example/.well-known/jwks.json"
), true);

$payload = JWT::decode($token, JWK::parseKeySet($jwks));

Python

import jwt
from jwt import PyJWKClient

jwks = PyJWKClient("https://api.authly.example/.well-known/jwks.json")
signing_key = jwks.get_signing_key_from_jwt(token)

payload = jwt.decode(
    token,
    signing_key.key,
    algorithms=["RS256"],
    issuer="https://api.authly.example",
    audience="your_client_id",
)

Verify with introspection

Sometimes you want Authly to answer. Introspection tells you if a token is live right now. It also reflects a revoked session, which an offline check cannot see until the token expires.

curl -X POST https://api.authly.example/api/v1/tokens/introspect \
  -H "X-Api-Key: YOUR_API_KEY" \
  -d token=ACCESS_TOKEN
{
  "active": true,
  "sub": "1042",
  "aud": "your_client_id",
  "exp": 1789999999,
  "email": "[email protected]"
}

An invalid or expired token returns { "active": false }. Authly reveals nothing else, so an attacker learns nothing from the response.

Which one to use

Verify offline for most requests. It is fast and needs no round trip. Add an introspection call for high value actions where instant revocation matters, such as a password change or a large payment.