Discovery & JWKS
Copy page
The self-description documents: /.well-known/openid-configuration (OIDC Discovery + RFC 8414) and /.well-known/jwks.json (RS256 public keys). How to verify EntryBit tokens offline.
Updated
Everything about the authorization server self-describes at two public, unauthenticated URLs. Point an OIDC library at the discovery document and it configures every endpoint itself; point a JWT verifier at the JWKS and it can validate EntryBit tokens offline, with no round-trip to EntryBit.
Discovery document
GET /.well-known/openid-configuration # OIDC Discovery 1.0
GET /.well-known/oauth-authorization-server # RFC 8414 — identical bodyBoth URLs return the same metadata. OIDC libraries look at the first; RFC 8414 clients look at the second.
curl https://entrybit.net/.well-known/openid-configurationKey fields:
| Field | Value / meaning |
|---|---|
issuer | https://entrybit.net |
authorization_endpoint | /api/oauth/authorize |
token_endpoint | /api/oauth/token |
userinfo_endpoint | /api/oauth/userinfo |
introspection_endpoint | /api/oauth/introspect |
revocation_endpoint | /api/oauth/revoke |
end_session_endpoint | /api/oauth/logout |
jwks_uri | /.well-known/jwks.json |
scopes_supported | The full scope catalog. |
response_types_supported | ["code"] |
code_challenge_methods_supported | ["S256"] — PKCE S256 only. |
subject_types_supported | ["public"] |
id_token_signing_alg_values_supported | ["RS256"] |
claims_supported | Identity claims, plus amr and auth_time. |
authorization_response_iss_parameter_supported | true (RFC 9207). |
With expo-auth-session, AuthSession.useAutoDiscovery('https://entrybit.net') fetches this and wires up every endpoint — including logout — for you.
JWKS
GET /.well-known/jwks.jsonThe JSON Web Key Set: the RS256 public keys that verify access tokens and id_tokens. Each key’s kid is its RFC 7638 thumbprint — match it against the JWT header’s kid to pick the right key.
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "<RFC 7638 thumbprint>",
"n": "<modulus>",
"e": "AQAB"
}
]
}Deploy note: these are backend routes, not static files. Behind a reverse proxy,
/.well-known/openid-configuration,/.well-known/oauth-authorization-server, and/.well-known/jwks.jsonmust all route to the EntryBit backend.
Verifying a token offline
EntryBit access tokens are self-contained RS256 JWTs (typ: at+jwt), so a resource server can authorize a request without calling EntryBit at all:
- Fetch and cache the JWKS. Refresh on an unknown
kid(keys can rotate). - Select the key whose
kidmatches the token header’skid. - Verify the signature with that RSA public key.
- Check the claims:
iss==https://entrybit.netexpin the future (andnbfin the past)typ==at+jwt(this gate blocks access-token / id-token confusion in both directions)aud== the expectedclient_id, if you pin it
- Enforce
scopefor the operation. See Scopes.
Use a maintained JWT library rather than hand-rolling RSA verification. In Node.js, jose covers steps 1–4 in a few lines:
// npm install jose
import { createRemoteJWKSet, jwtVerify } from 'jose';
const ISSUER = 'https://entrybit.net';
// Step 1 — fetched once, cached, and refetched automatically when a token
// arrives signed by an unknown kid (key rotation).
const JWKS = createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`));
export async function verifyAccessToken(token: string, requiredScope: string) {
// Steps 2–4 — signature, iss, exp/nbf, and typ, all enforced by jose.
const { payload } = await jwtVerify(token, JWKS, {
issuer: ISSUER,
algorithms: ['RS256'],
typ: 'at+jwt', // blocks an id_token posing as an access token
audience: 'eb_9f1c2ab34cd56ef7', // your client_id — omit if you don't pin aud
});
// Step 5 — enforce scope for the operation.
const scopes = typeof payload.scope === 'string' ? payload.scope.split(' ') : [];
if (!scopes.includes(requiredScope)) {
throw new Error(`missing scope: ${requiredScope}`); // respond 403 insufficient_scope
}
return payload; // { iss, sub, aud, client_id, scope, iat, nbf, exp, jti }
}// In a request handler:
const claims = await verifyAccessToken(bearerToken, 'passes:read');
// claims.sub is the EntryBit user id — authorize the operation against it.If you prefer a live check over local verification — or need to inspect an opaque refresh token — a confidential client can use Introspection instead.