Guides
Verify JWTs (any language)
Raw HTTP + any JOSE library. Useful for Ruby, PHP, .NET, Rust, or any stack without a first-party Avnology SDK.
Verify JWTs (any language)
Not on TypeScript / Go / Python? The JWKS endpoint is standard OIDC. Any mainstream JOSE library can verify tokens against it. Below is the recipe.
Step 1: Fetch and cache the JWKS
GET https://api-id.avnology.net/.well-known/jwks.json
{
"keys": [
{
"kid": "2026-04-15",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"n": "…",
"e": "AQAB"
Rules:
- Cache for up to 5 minutes.
- Respect
Cache-Control: max-age. - On a verify failure with
kidnot in the cached set, refetch once (rate-limit refetches to once per 30 seconds to prevent JWKS DoS).
Step 2: Verify the token
Decode the JWT header, find the matching kid in the JWKS, verify the signature. Enforce:
| Claim | Rule |
|---|---|
alg | Header must be RS256, RS384, RS512, ES256, ES384, or ES512. Reject none and HS256. |
iss | Must equal the issuer you expect (e.g. https://api-id.avnology.net) |
aud | Must contain your configured audience (if set) |
exp | Must be in the future, allowing 30s skew |
nbf | If present, must be in the past, allowing 30s skew |
iat | Informational. Don't use for freshness checks. |
Reject the token if any check fails.
Step 3: Extract claims
Avnology JWTs carry these payload fields (no Ory terminology):
| Claim | Type | Description |
|---|---|---|
sub | string | Identity ID, usr_… |
user_id | string | Alias of sub |
organization_id | string | org_… if scoped |
session_id | string | ses_… |
scope | space-separated string | OAuth scopes |
permissions | string[] | Resolved Keto permissions |
aal | string | Assurance level (aal1, aal2, aal3) |
iss | string | Issuer base URL |
aud | string | string[] |
iat, exp, nbf | int | Unix seconds |
Language-specific recipes
Ruby (jwt gem)
require "jwt"
require "net/http"
require "json"
JWKS_URI = URI("https://api-id.avnology.net/.well-known/jwks.json")
EXPECTED_ISSUER = "https://api-id.avnology.net"
EXPECTED_AUDIENCE = "my-api"
def jwks
@jwks ||= JSON.parse(Net::HTTP.get(JWKS_URI
PHP (firebase/php-jwt)
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
$jwks = json_decode(
file_get_contents("https://api-id.avnology.net/.well-known/jwks.json"),
true,
);
$keys = JWK::parseKeySet($jwks);
$claims =
.NET (Microsoft.IdentityModel.Tokens)
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.
Rust (jsonwebtoken)
Common pitfalls
- Don't verify with
HS256. Avnology only issues asymmetrically-signed tokens. Reject the header'salgif it'sHS256ornone. - Don't skip
expverification "just during testing." Every JOSE library has a sandbox mode that omitsexp; it's a footgun in production. - Cache the JWKS. Re-fetching per request will melt your gateway and get you rate-limited.
- Handle key rotation. On
kidmiss, refetch once. Don't hard-fail.
Related
- Verify JWTs (TypeScript) -- shipped SDK
- Verify JWTs (Go) -- shipped SDK
- Verify JWTs (Python) -- shipped SDK
- JWKS endpoint
- OIDC discovery