Skip to content

Discovery & JWKS

העתקת עמוד

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.

עודכן

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 body

Both 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-configuration

Key fields:

FieldValue / meaning
issuerhttps://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_supportedThe 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_supportedIdentity claims, plus amr and auth_time.
authorization_response_iss_parameter_supportedtrue (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.json

The 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.json must 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:

  1. Fetch and cache the JWKS. Refresh on an unknown kid (keys can rotate).
  2. Select the key whose kid matches the token header’s kid.
  3. Verify the signature with that RSA public key.
  4. Check the claims:
    • iss == https://entrybit.net
    • exp in the future (and nbf in the past)
    • typ == at+jwt (this gate blocks access-token / id-token confusion in both directions)
    • aud == the expected client_id, if you pin it
  5. Enforce scope for 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.