Skip to main content
Every member.dev hub is a standards-compliant OpenID Connect (OIDC) Identity Provider. Your external application registers as an OAuth client, and hub members can sign in to your tool without ever leaving the hub experience — one click, their hub session carries them in. This guide walks through:
  1. Discovering the hub’s OIDC configuration
  2. Registering an OAuth client (hub owner)
  3. Running the Authorization Code + PKCE flow (your app)
  4. Verifying the id_token and calling UserInfo
  5. Revoking tokens

1. OIDC discovery

Every hub exposes a discovery document at the well-known URL. Fetch it once at startup and cache it:
Key fields: Fetch the JWKS from jwks_uri to verify id_token signatures:
The JWKS is a standard RSA public-key set. Use any OIDC/JWT library to verify RS256 signatures.

2. Register an OAuth client (hub owner)

Hub owners register OAuth clients via the platform API. You need a platform JWT (creator login) or a team API key with write access.
Public (is_public: true) clients use PKCE instead of a client secret. No secret is returned or stored.

Register a confidential client (server-to-server / backend apps)

Store client_secret now — this is the ONLY time it is returned. Confidential clients must present it on every token request via HTTP Basic auth (client_secret_basic).

Create request attributes

Add or remove redirect URIs later

Redirect URIs must use HTTPS, contain no fragment (#), and no userinfo component. The redirect_uri in the authorization request must exactly match one of the registered URIs — any mismatch returns a 400 error (never a redirect to an unregistered URI, per RFC 6749 §10.6).

3. The Authorization Code + PKCE flow

Step 1: build PKCE parameters

Generate a cryptographically random code_verifier (43–128 URL-safe chars), then compute:
Most OAuth libraries (e.g. pkce-challenge, oauth4webapi) do this for you.

Step 2: redirect to the authorization endpoint

Parameters: What happens next:
  • /oauth/authorize reads the member’s hub session cookie (as well as a Bearer token if present). A browser top-level redirect therefore works for already-logged-in members without any extra step — this is the one-click SSO path.
  • If the member has an active hub session → the server checks consent. First-party clients (set by platform admins) auto-skip consent. Third-party clients show a consent screen.
  • If consent is required → the browser receives a 200 with a consent_request_id. Your consent UI submits it to POST /oauth/consent.
  • If no session is present → returns login_required (this is the only error returned in the no-session case, including when prompt=noneinteraction_required is not used). The hub’s front end should handle this by starting a login flow (magic link, external login, etc.) and then retrying the authorization. Automatic magic-link bounce is a planned fast-follow.
  • On approval → 302 to your redirect_uri with code, state, and iss.

Step 3: exchange the code for tokens

For confidential clients, add HTTP Basic auth:
Response:

Refresh the access token


4. Verify the id_token

The id_token is a signed RS256 JWT. Verify it with any standard OIDC library using the JWKS from /.well-known/jwks.json. Verification checklist (per OIDC Core §3.1.3.7):
  1. Signature valid against the published JWKS
  2. iss = https://api.member.dev
  3. aud contains your client_id
  4. exp has not passed
  5. nonce matches the value you sent in the authorization request
Claims: Important: the sub claim is the member’s contact ID within this hub — not a synthetic platform-level user ID. Your app should store sub as the foreign key linking to the member.

5. Call the UserInfo endpoint

For fresher claims after the initial login, call UserInfo with the access token:
The access token audience must be mio-oauth. Standard member JWT tokens (audience mio-api) are rejected with 401. Use only tokens obtained from POST /oauth/token. Response (openid + profile + email scopes):

6. Revoke a token

Revoke an access or refresh token when the member logs out of your app (RFC 7009):
The server always returns 200 regardless of whether the token was found — no information about token existence is leaked. Revoking a refresh token also invalidates all sibling tokens issued in the same refresh family.

7. Worked example: one-click SSO

Scenario: Alice is a member of “Lab Creator” hub (run by Bob). Bob builds an external experiment-tracking tool and wants Alice to log in with her hub account.

Manage clients


Security notes

  • PKCE is mandatory — missing or non-S256 code_challenge_method returns an authorization error.
  • Exact redirect_uri match — any mismatch returns a 400 JSON error body (never a redirect), preventing open redirector attacks.
  • hub_id is server-derived — the hub is resolved from the OAuth client record, never from user-supplied parameters.
  • Token audience — oauth access tokens carry aud: mio-oauth. They are rejected on all non-oauth endpoints; standard member API tokens are rejected at the userinfo/revoke endpoints.
  • Revocation ownership — a client can only revoke tokens it issued. Revocation verifies the token’s RS256 signature before any state changes, so a forged token cannot blacklist a victim’s session.