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://app.ageonce.com/api/oauth/jwksResponse
{
"keys": [
{
"kty": "RSA",
"kid": "ageonce-public-key",
"use": "sig",
"alg": "RS256",
"n": "base64url_encoded_modulus...",
"e": "AQAB"
}
]
}Key fields
| Field | Description |
|---|---|
kty | Key type (always "RSA") |
kid | Key identifier |
use | Purpose ("sig" for signing) |
alg | Algorithm (RS256) |
n | RSA modulus (base64url) |
e | RSA exponent (base64url) |
Usage
Node.js (jwks-rsa)
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');
const client = jwksClient({
jwksUri: 'https://app.ageonce.com/api/oauth/jwks',
cache: true,
cacheMaxAge: 86400000, // Cache 24 hours
rateLimit: true,
jwksRequestsPerMinute: 10,
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
// Token validation
jwt.verify(token, getKey, {
algorithms: ['RS256'],
issuer: 'ageonce',
}, (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://app.ageonce.com/api/oauth/jwks')
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='ageonce'
)
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://app.ageonce.com/api/oauth/jwks'),
true
);
// Convert to keys
$keys = JWK::parseKeySet($jwks);
// Validation
try {
$payload = JWT::decode($token, $keys);
if ($payload->iss !== 'ageonce') {
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://app.ageonce.com/api/oauth/jwks", 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
| Parameter | Recommended value |
|---|---|
| Cache TTL | 24 hours |
| Max requests/min | 10 |
| Retry on error | 3 attempts |
Key updates
Keys are updated rarely (usually once a year). When updating:
- New key is added to JWKS
- Old key remains for transition period
- Tokens are signed with new key
- Old key is removed after a month
kid (Key ID) in JWT header indicates which key to use for validation.
Errors
| HTTP code | Description |
|---|---|
| 200 | Success |
| 500 | Internal server error |