- Discovering the hub’s OIDC configuration
- Registering an OAuth client (hub owner)
- Running the Authorization Code + PKCE flow (your app)
- Verifying the id_token and calling UserInfo
- Revoking tokens
1. OIDC discovery
Every hub exposes a discovery document at the well-known URL. Fetch it once at startup and cache it:
Fetch the JWKS from
jwks_uri to verify id_token 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.Register a PKCE public client (recommended for SPAs and native apps)
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)
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
#), 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 randomcode_verifier (43–128 URL-safe chars), then compute:
pkce-challenge, oauth4webapi) do this for you.
Step 2: redirect to the authorization endpoint
What happens next:
/oauth/authorizereads 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
200with aconsent_request_id. Your consent UI submits it toPOST /oauth/consent. - If no session is present → returns
login_required(this is the only error returned in the no-session case, including whenprompt=none—interaction_requiredis 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 →
302to yourredirect_uriwithcode,state, andiss.
Step 3: exchange the code for tokens
Refresh the access token
4. Verify the id_token
Theid_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):
- Signature valid against the published JWKS
iss=https://api.member.devaudcontains yourclient_idexphas not passednoncematches the value you sent in the authorization request
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: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):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_methodreturns an authorization error. - Exact redirect_uri match — any mismatch returns a
400JSON 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.