Authorization Code Flow with PKCE
Muhajir Studio supports OpenID Connect through Authorization Code Flow with PKCE. Admin-created applications are Public PKCE clients: they use a public client_id, do not have a client_secret, and must prove possession of a code_verifier during token exchange.
Base URLs Used in This Flow
| Service | Base URL | Role in this flow |
|---|---|---|
| API | https://api.muhajirstudio.com/ | /api/auth/oauth2/authorize, /api/auth/oauth2/token, UserInfo, JWKS, and public metadata. |
| Main web | https://login.muhajirstudio.com/ | Login, signup, email verification, and consent UI. |
| Admin web | https://admin.muhajirstudio.com/ | Client registration and scope/redirect configuration. |
Provider Requirements
The OIDC provider is configured with:
requirePKCE: trueallowPlainCodeChallengeMethod: falsedefaultScope: openid- supported scopes from
OIDC_SCOPES - JWT-backed OIDC support
- issuer from
OIDC_ISSUER
Only code_challenge_method=S256 is supported. Do not use plain.
1. Prepare Client Configuration
Ask an admin for the values registered in web-admin:
| Value | Notes |
|---|---|
client_id | Generated public client identifier. |
| Redirect URI | Must exactly match one derived registered redirectUri. |
| Allowed scopes | The application scope set selected by the admin. |
| Issuer | The configured OIDC_ISSUER value. |
Admin-created applications never receive a client_secret.
2. Generate Per-Request Values
For every login attempt, generate and store:
| Value | Purpose |
|---|---|
code_verifier | Secret random string kept by the client until token exchange. |
code_challenge | Base64url SHA-256 digest of code_verifier. |
state | CSRF protection value that must round-trip. |
nonce | Replay protection value to validate in the id_token. |
Store state, nonce, and code_verifier in a server session or another storage mechanism appropriate for your application type.
3. Start Authorization
Redirect the browser to https://api.muhajirstudio.com/api/auth/oauth2/authorize:
GET /api/auth/oauth2/authorize
Example query:
client_id=CLIENT_ID
redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback
response_type=code
scope=openid%20profile%20email
state=STATE
nonce=NONCE
code_challenge=CODE_CHALLENGE
code_challenge_method=S256
Required protocol parameters:
| Parameter | Requirement |
|---|---|
client_id | Public client ID registered in web-admin. |
redirect_uri | Exact registered full URI. |
response_type | Must be code. |
scope | Space-separated scopes; defaults to openid when omitted by provider behavior. |
code_challenge | PKCE S256 challenge. |
code_challenge_method | Must be S256. |
Recommended parameters:
| Parameter | Requirement |
|---|---|
state | Strongly recommended for CSRF protection. |
nonce | Strongly recommended when using or validating the id_token. |
web-main preserves these OIDC parameters through login, signup, email verification, and the email-verified screen: client_id, redirect_uri, response_type, scope, state, code_challenge, code_challenge_method, nonce, prompt, max_age, and login_hint.
4. Authorization Request Processing
Before the OIDC provider handles the authorize request, Muhajir Studio applies three server-side checks.
Redirect URI Validation
For dynamic applications, the server loads the application by client_id and validates that redirect_uri is in the application's derived redirectUris list.
Static trusted clients, such as the reserved web-admin client, are validated against their configured redirect URLs.
If validation fails, the response is:
{
"error": "INVALID_REDIRECT_URI",
"message": "redirect_uri is not registered for this client"
}
Scope Constraint
For dynamic applications with configured scopes, the server rewrites the requested scope to the application's registered scope set before the OIDC provider processes the request.
This means the admin-selected scopes are the source of truth for dynamic applications.
Email Verification Gate
If the user is signed in but emailVerified is not true, the server redirects to /verify-email on web-main and preserves the original authorize query parameters. The user cannot complete authorization until email verification is done.
5. User Login and Consent
The OIDC provider uses configured loginPage and consentPage URLs. The user-facing flow can include:
- Login or signup.
- Email verification.
- Consent.
- Redirect back to the client callback.
The consent page loads public application metadata from GET /api/public/applications/{clientId}. When a redirect_uri query value exists, that value is forwarded so the metadata endpoint can safely derive any post-verification redirect URI.
When a signed-in user chooses to switch accounts from the consent page, web-main signs out the current session and returns to the registered application's post-verification URL when one is available. The client application should then start a fresh authorization request with a new state and PKCE challenge.
Approval is disabled when application metadata cannot be loaded.
6. Handle the Callback
After approval, Muhajir Studio redirects to the registered redirect_uri with an authorization code and the original state.
Your callback handler must:
- Verify
statematches the stored value. - Retrieve the stored
code_verifier. - Exchange the code at the token endpoint.
- Validate
id_tokenclaims when using the ID token for local sign-in.
7. Exchange the Authorization Code
Send a form-encoded token request:
POST /api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=AUTHORIZATION_CODE&code_verifier=CODE_VERIFIER&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback
Do not send client_secret for admin-created Public PKCE clients.
Successful token responses can include:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "...",
"refresh_token": "...",
"scope": "openid profile email"
}
Use the access token as Authorization: Bearer ACCESS_TOKEN when calling OIDC UserInfo or /api/external/* APIs.
8. Refresh Tokens
The token endpoint also supports grant_type=refresh_token where supported by the OIDC provider and granted scopes/client configuration. The offline_access scope is intended for refresh-token based sessions where supported.
Third-party clients should use the standard token endpoint directly unless explicitly instructed to use a first-party cookie helper.
Security Checklist
- Use HTTPS redirect URIs outside local development.
- Generate new
state,nonce, and PKCE values for every login attempt. - Validate
statebefore exchanging the code. - Validate
id_tokenissuer, audience, expiry, signature, andnoncewhen using it for sign-in. - Never use or store a client secret for admin-created Public PKCE clients.
- Store tokens according to your app type and threat model.