JWT Configuration
Authara issues short-lived JWT access tokens signed with Ed25519 (alg=EdDSA).
Core holds the private signing seeds; applications and SDKs validate tokens
with public keys from Authara's JWKS endpoint. A relying application never
needs access to private signing material.
See also: Configuration Reference
Configuration
AUTHARA_JWT_ISSUER=authara
AUTHARA_JWT_ACTIVE_KEY_ID=key-2026-01
AUTHARA_JWT_PRIVATE_KEYS=key-2026-01:BASE64_ED25519_SEED
AUTHARA_ACCESS_TOKEN_TTL_MINUTES=10
AUTHARA_JWT_ISSUER becomes the token's iss claim. Validators must compare
it exactly with their configured expected issuer.
AUTHARA_JWT_ACTIVE_KEY_ID selects the key used for new tokens. It must exist
in AUTHARA_JWT_PRIVATE_KEYS, and its ID is included in the JWT header as
kid.
AUTHARA_JWT_PRIVATE_KEYS is a comma-separated map:
keyID:base64Ed25519Seed,keyID2:base64Ed25519Seed2
Every seed must decode to exactly 32 bytes. Generate one with:
openssl rand -base64 32
Keep private seeds in a secret manager, use different keys per environment, and never provide this setting to an application or SDK.
Public JWKS
Core publishes the corresponding public keys at:
GET /auth/.well-known/jwks.json
Each JWK is an OKP/Ed25519 verification key with alg=EdDSA, use=sig,
and the matching kid. The response contains no private d value. It is
publicly cacheable for five minutes and supports conditional requests with an
ETag.
Configure an SDK with Authara's base URL or the explicit JWKS URL. Validation
must still check the signature, kid, algorithm, issuer, audience, expiry,
issued-at time, and required Authara claims. JWKS is key metadata, not an
introspection endpoint and not an OpenID Connect discovery document.
Key rotation
Rotation overlaps the old and new public keys. For the smoothest rollout, publish the new key before using it:
- Generate a new 32-byte seed.
- Add it to
AUTHARA_JWT_PRIVATE_KEYSwithout changing the active key, then restart Core. - Wait at least the five-minute JWKS cache lifetime so validators learn the new public key.
- Set
AUTHARA_JWT_ACTIVE_KEY_IDto the new ID and restart Core again. - Keep the old key configured for at least the maximum access-token lifetime, plus cache and clock-skew allowance.
- Remove the old key and restart Core after that window.
For example:
AUTHARA_JWT_ACTIVE_KEY_ID=key-2026-06
AUTHARA_JWT_PRIVATE_KEYS=key-2026-06:BASE64_NEW_SEED,key-2026-01:BASE64_OLD_SEED
SDKs normally use the cached JWKS. When a token arrives with a new kid, the
Authara Go SDK performs one rate-limited refresh immediately, so rollout does
not have to wait for normal cache expiry.
Do not remove the old key at the same time you activate the new one. An already-issued token remains valid until expiry and must retain a matching public key throughout that bounded lifetime.
Rotation with multiple Core replicas
Every Core replica behind a load balancer must use the same private-key set and active key. A replica that signs with a new key while another replica still publishes only the old JWKS can create tokens that validators intermittently reject, depending on which replica serves the JWKS request.
Use two fleet-wide rollouts:
- Add the new private key to every replica while the old key remains active.
- Confirm every replica publishes both public keys, then wait at least the five-minute JWKS cache lifetime.
- Change the active key to the new ID on every replica, keeping both keys.
- After the old-token lifetime, JWKS cache lifetime, and clock-skew allowance have elapsed, remove the old key from every replica.
Do not activate a key during the first rolling deployment. Complete and verify the fleet-wide prepublication step before any replica signs with the new key.
Token lifetime and revocation
AUTHARA_ACCESS_TOKEN_TTL_MINUTES defaults to 10 and may be between 1 and
1440 minutes. Expiry-only revocation mode imposes the stricter 10-minute
maximum. With immediate revocation, SDK middleware can additionally query the
shared Redis revocation records after local signature verification.
JWKS changes only how the signature is verified. It does not replace Authara's existing session, refresh-token, or Redis revocation behavior.
Migration from shared HS256 keys
Deployments upgrading from an HS256-based Core version must replace the removed
AUTHARA_JWT_KEYS setting with AUTHARA_JWT_PRIVATE_KEYS, and
configure relying services with Authara's base URL or JWKS URL instead of a
shared secret.
If Core and relying services are deployed independently, release the JWKS-capable SDK first, update each service, and then deploy the EdDSA-signing Core version. There is intentionally no dual-algorithm period: accepting both HS256 and EdDSA would preserve the secret-sharing design and widen the algorithm-confusion surface. Use a coordinated deployment after every relying service can validate EdDSA tokens through JWKS.