1. Problem
go-api access tokens are long-lived (validity of up to one year — a hard client
requirement). stac-auth-proxy currently validates them locally only: JWT signature
against the go-api OIDC JWKS, plus expiry (exp). Local validation cannot detect a token
that go-api has revoked before its natural expiry, so a leaked or withdrawn token
stays valid for up to a year. stac-auth-proxy needs to honor revocation.
2. Approach
Every token carries a unique jti (JWT ID) claim. go-api tracks revocation state
centrally and exposes a lightweight verification (introspection) endpoint: given a
jti, it reports whether the token is still active. stac-auth-proxy keeps all its
existing local checks and adds one call to this endpoint.
- Local validation (unchanged): signature +
exp, via the go-api JWKS.
- New step: confirm the token's
jti is still active.
3. What go-api Provides
Already available:
- Every token is tracked centrally by its
jti, so a token can be identified and acted
on individually after it has been issued.
- Tokens carry
jti and exp claims.
Not yet available:
- A revocation flag per token. Administrators can revoke (and un-revoke) a token from
the go-api admin; revocation takes effect immediately on go-api's side.
- Self-service token management from go-web-app. Users can revoke
their own individual tokens directly from go-web-app.
- The verification endpoint (Section 4) that reports a token as inactive when it is
revoked, expired, superseded, or unrecognized — a single active: true/false answer.
- An
iat (issued-at) claim added to newly issued tokens. iat is informational and
not required for validation.
4. Verification Endpoint Contract
| Item |
Value |
| Method |
POST |
| URL |
https://goadmin.ifrc.org/api/v2/external-token/verify/ |
| Request body |
{"jti": "<jti-from-token>"} |
| Response |
HTTP 200 with {"active": true} or {"active": false} |
Malformed jti |
HTTP 400 (must be a valid UUID) |
| Auth |
None required — consistent with go-api's other public endpoints |
POST https://goadmin.ifrc.org/api/v2/external-token/verify/
Content-Type: application/json
{ "jti": "<jti-from-token>" }
Semantics:
active: true — not revoked; allow (subject to stac-auth-proxy's existing checks).
active: false — reject. This covers revoked tokens and any jti go-api does not
recognize (unknown jti → {"active": false}, definitively).
- Malformed
jti (not a UUID) → HTTP 400.
Auth: the endpoint requires no credential, consistent with go-api's other public
endpoints. It returns only a boolean and is keyed by an unguessable UUID (jti).
5. What stac-auth-proxy Implements
Per incoming request (framework-agnostic):
- Local validation — signature +
exp via JWKS. Reject on failure, as today.
- Extract
jti from the validated token. A token with no jti cannot be checked for
revocation — coordinate handling with go-api (see fail policy).
- Check the cache for this
jti; on a usable "active" hit, proceed.
- On a cache miss, call the endpoint with the
jti.
- Enforce:
active: true → allow (and cache); active: false → reject with 401 / 403.
Caching (required)
- Cache positive results only, so a revoked token is never served stale.
- Short TTL, 1-5 m. Revocation then takes effect within at most one TTL window.
- Do not cache positive results with a long TTL — it defeats revocation.
Failure policy (endpoint unreachable)
- Fail-closed (recommended): reject on outage. An outage should not let a
possibly-revoked token through; the tradeoff is reduced STAC availability.
- Fail-open: allow on outage. Better availability, but a revoked token can slip through.
go-api recommends fail-closed, but leaves the final call to stac-auth-proxy.
Fail-closed plus the short positive cache above is a reasonable middle ground:
recently-verified tokens keep working briefly through a transient outage, while unverified
tokens are held.
6. Request Lifecycle
Client ──(request + Bearer JWT)──▶ stac-auth-proxy
│
1. Verify JWT signature (JWKS) + expiry (exp)
│ fail ─▶ 401
▼
2. Extract `jti` claim
│
3. Look up `jti` in local revocation cache
│
┌──────────hit "active"─┴─miss/expired──────────┐
▼ ▼
proceed 4. POST { jti } ──▶ go-api
│ verify endpoint
│ │
│ {"active": true|false} ◀┘
│ │
│ 5a. active:true ─▶ cache (short TTL), proceed
│ 5b. active:false ─▶ 401/403
▼ │
forward to STAC backend ◀───────────────────────────────┘
1. Problem
go-api access tokens are long-lived (validity of up to one year — a hard client
requirement). stac-auth-proxy currently validates them locally only: JWT signature
against the go-api OIDC JWKS, plus expiry (
exp). Local validation cannot detect a tokenthat go-api has revoked before its natural expiry, so a leaked or withdrawn token
stays valid for up to a year. stac-auth-proxy needs to honor revocation.
2. Approach
Every token carries a unique
jti(JWT ID) claim. go-api tracks revocation statecentrally and exposes a lightweight verification (introspection) endpoint: given a
jti, it reports whether the token is still active. stac-auth-proxy keeps all itsexisting local checks and adds one call to this endpoint.
exp, via the go-api JWKS.jtiis still active.3. What go-api Provides
Already available:
jti, so a token can be identified and actedon individually after it has been issued.
jtiandexpclaims.Not yet available:
the go-api admin; revocation takes effect immediately on go-api's side.
their own individual tokens directly from go-web-app.
revoked, expired, superseded, or unrecognized — a single
active: true/falseanswer.iat(issued-at) claim added to newly issued tokens.iatis informational andnot required for validation.
4. Verification Endpoint Contract
POSThttps://goadmin.ifrc.org/api/v2/external-token/verify/{"jti": "<jti-from-token>"}HTTP 200with{"active": true}or{"active": false}jtiHTTP 400(must be a valid UUID){ "active": true }Semantics:
active: true— not revoked; allow (subject to stac-auth-proxy's existing checks).active: false— reject. This covers revoked tokens and anyjtigo-api does notrecognize (unknown
jti→{"active": false}, definitively).jti(not a UUID) →HTTP 400.Auth: the endpoint requires no credential, consistent with go-api's other public
endpoints. It returns only a boolean and is keyed by an unguessable UUID (
jti).5. What stac-auth-proxy Implements
Per incoming request (framework-agnostic):
expvia JWKS. Reject on failure, as today.jtifrom the validated token. A token with nojticannot be checked forrevocation — coordinate handling with go-api (see fail policy).
jti; on a usable "active" hit, proceed.jti.active: true→ allow (and cache);active: false→ reject with 401 / 403.Caching (required)
Failure policy (endpoint unreachable)
possibly-revoked token through; the tradeoff is reduced STAC availability.
go-api recommends fail-closed, but leaves the final call to stac-auth-proxy.
Fail-closed plus the short positive cache above is a reasonable middle ground:
recently-verified tokens keep working briefly through a transient outage, while unverified
tokens are held.
6. Request Lifecycle