AgeOnce Docs
API Reference

JWKS Endpoint

JSON Web Key Set for local token validation

JWKS Endpoint

Endpoint for retrieving public keys for local JWT token validation.

Endpoint

GET https://api.ageonce.com/api/.well-known/jwks.json

Response

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "key_2025_01",
      "use": "sig",
      "alg": "RS256",
      "n": "base64url_encoded_modulus...",
      "e": "AQAB"
    }
  ]
}

Key fields

FieldDescription
ktyKey type (always "RSA")
kidKey identifier
usePurpose ("sig" for signing)
algAlgorithm (RS256)
nRSA modulus (base64url)
eRSA exponent (base64url)

Usage

Node.js (jwks-rsa)

const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');

const client = jwksClient({
  jwksUri: 'https://api.ageonce.com/api/.well-known/jwks.json',
  cache: true,
  cacheMaxAge: 86400000, // Cache 24 hours
  rateLimit: true,
  jwksRequestsPerMinute: 10,
});

function getKey(header, callback) {
  client.getSigningKeys((err, keys) => {
    if (err) return callback(err);
    const signingKey = keys[0].getPublicKey();
    callback(null, signingKey);
  });
}

// Token validation
jwt.verify(token, getKey, {
  algorithms: ['RS256'],
  issuer: 'https://ageonce.io',
}, (err, decoded) => {
  if (err) {
    console.error('Token invalid:', err.message);
  } else {
    console.log('Token valid:', decoded);
  }
});

Python (PyJWT)

import jwt
import requests
from jwt.algorithms import RSAAlgorithm

# Fetch JWKS
jwks_response = requests.get('https://api.ageonce.com/api/.well-known/jwks.json')
jwks = jwks_response.json()

# Convert JWK to PEM
key_data = jwks['keys'][0]
public_key = RSAAlgorithm.from_jwk(key_data)

# Validation
try:
    payload = jwt.decode(
        token,
        public_key,
        algorithms=['RS256'],
        issuer='https://ageonce.io'
    )
    print('Token valid:', payload)
except jwt.InvalidTokenError as e:
    print('Token invalid:', str(e))

PHP (firebase/php-jwt)

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

// Fetch JWKS
$jwks = json_decode(
    file_get_contents('https://api.ageonce.com/api/.well-known/jwks.json'),
    true
);

// Convert to keys
$keys = JWK::parseKeySet($jwks);

// Validation
try {
    $payload = JWT::decode($token, $keys);
    
    if ($payload->iss !== 'https://ageonce.io') {
        throw new Exception('Invalid issuer');
    }
    
    echo 'Token valid: ' . print_r($payload, true);
} catch (Exception $e) {
    echo 'Token invalid: ' . $e->getMessage();
}

Go

import (
    "github.com/golang-jwt/jwt/v5"
    "github.com/MicahParks/keyfunc/v2"
)

// Create JWKS client
jwks, err := keyfunc.Get("https://api.ageonce.com/api/.well-known/jwks.json", keyfunc.Options{
    RefreshInterval: time.Hour * 24,
})
if err != nil {
    log.Fatal(err)
}

// Validation
token, err := jwt.Parse(tokenString, jwks.Keyfunc, jwt.WithValidMethods([]string{"RS256"}))
if err != nil {
    log.Println("Token invalid:", err)
} else if claims, ok := token.Claims.(jwt.MapClaims); ok {
    log.Println("Token valid:", claims)
}

Caching

Always cache JWKS! Making a request on every validation is inefficient.

Recommendations

ParameterRecommended value
Cache TTL24 hours
Max requests/min10
Retry on error3 attempts

Key id

The published JWK uses kid key_2025_01. Access tokens are signed with RS256 and do not set kid in the JWT header, so validate with this key instead of selecting by header key id.

Errors

HTTP codeDescription
200Success
500Internal server error

On this page