Skip to main content

Security Best Practices

Use these practices when integrating third-party applications with Muhajir Studio in production.

Production Service Boundaries

ServiceProduction URLSecurity role
APIhttps://api.muhajirstudio.com/OIDC endpoints, token exchange, JWKS, public metadata, and external APIs.
Main webhttps://login.muhajirstudio.com/Hosted user login, signup, email verification, and consent.
Admin webhttps://admin.muhajirstudio.com/Application registration, scope selection, and RBAC-protected administration.

Keep these services distinct in client configuration. External integrations should call only documented OIDC, public metadata, and /api/external/* endpoints.

Use Authorization Code Flow with PKCE

Admin-created applications are Public PKCE clients:

  • client_id is public.
  • client_secret is not issued.
  • code_challenge_method must be S256.
  • Plain PKCE challenges are disabled.
  • Each login attempt must generate fresh state, nonce, and code_verifier values.

Validate state before exchanging the authorization code. Treat an invalid state as a failed login attempt and restart the flow.

Register Exact Redirects and Origins

Muhajir Studio derives redirect URIs from two registered values:

Registered valueExampleRules
Allowed web originhttps://app.example.comOrigin only; no path, query, fragment, or wildcard.
Redirect path/auth/callbackPath only; no host, query, or fragment.

The resulting redirect URI must match exactly, for example https://app.example.com/auth/callback.

Security recommendations:

  • Use HTTPS origins in production.
  • Register only origins you control.
  • Keep callback paths dedicated to OAuth/OIDC handling.
  • Do not accept callback URLs from untrusted request parameters.
  • For mobile apps, use HTTPS Universal Links or Android App Links rather than custom URL schemes.

Request Least-Privilege Scopes

Admin-selected application scopes are the source of truth. During authorization, Muhajir Studio constrains the effective requested scope set to the application's registered scopes.

Use only scopes your integration actually needs:

ScopeGives access to
openidStable subject identifier; required for /api/external/me.
profileBasic profile fields such as name and picture.
emailEmail address and verification status.
permissionsAssigned permission keys where supported.
offline_accessRefresh-token based sessions where supported.

After a scope change, users must start a new authorization flow to receive tokens with the updated grants.

Validate Tokens Correctly

Use the right token for the right purpose:

TokenIntended use
Access tokenSend to /api/external/* with Authorization: Bearer ACCESS_TOKEN.
ID tokenValidate user authentication claims in your client or backend. Do not send it to external APIs.
Refresh tokenStore securely and use only for refreshing sessions.

When validating ID tokens, check at least:

  • issuer matches the production issuer
  • audience matches your client_id
  • expiration has not passed
  • nonce matches the value created for the login attempt when you requested one

Fetch signing keys from the JWKS endpoint advertised by OIDC discovery instead of hardcoding keys.

Store Tokens by Application Type

Application typeRecommended storage
Backend web appStore tokens server-side; give the browser only your application session cookie.
Browser SPAPrefer in-memory access-token storage; persistent browser storage is an explicit XSS trade-off.
Mobile appStore tokens in platform secure storage such as Keychain or encrypted credential storage.

Never log tokens, authorization codes, refresh tokens, or ID tokens. Redact them from analytics, crash reports, support tickets, and browser console output.

Use External APIs with Bearer Tokens

Third-party integrations should use /api/external/* as the stable external API namespace.

GET /api/external/me
Authorization: Bearer ACCESS_TOKEN

Cookie-only sessions are not accepted by /api/external/*. A missing, expired, or invalid bearer access token returns 401 UNAUTHORIZED. A valid token without required scopes returns 403 FORBIDDEN with missingScopes where available.

Understand Browser CORS Rules

Browser-based third-party calls are allowed only from registered application origins.

Dynamic third-party CORS applies to:

  • POST /api/auth/oauth2/token
  • OPTIONS /api/auth/oauth2/token
  • /api/external/*

It is intentionally non-credentialed:

SettingValue
Credentialsfalse
MethodsGET, POST, OPTIONS
Allowed headersAuthorization, Content-Type
Registered-origin cache60 seconds

Do not use credentials: 'include' from third-party browser clients when calling token or external API endpoints. Send bearer tokens explicitly.

Protect Admin Access with Permissions

Admin surfaces are permission-protected. The admin API requires login-web-admin for admin-only namespaces and feature-specific permissions such as:

  • manage-users
  • manage-roles
  • manage-services
  • manage-authentication
  • manage-branding
  • manage-applications

Do not rely on UI hiding as a security boundary. Backend permission checks are the source of truth.

When editing a role assigned to your own account, the admin API rejects permission changes that would remove your own login-web-admin or manage-roles access. Use another administrator account for intentional admin-access transfers or emergency recovery.

Keep First-Party and Third-Party Flows Separate

First-party web applications can use cookie-backed helper endpoints where explicitly documented, such as the cookie token wrapper used by web-admin. Third-party integrations should use the standard OIDC token endpoint and bearer-token external APIs unless instructed otherwise.

This separation prevents browser integrations from depending on internal cookie-session assumptions.

Security Checklist

  • Use HTTPS for all production redirect origins.
  • Use Authorization Code Flow with PKCE and S256.
  • Generate fresh state, nonce, and PKCE values for every login.
  • Validate state and, when using ID tokens, validate nonce.
  • Register exact origins and redirect paths only.
  • Request least-privilege scopes.
  • Store tokens according to application type.
  • Send access tokens only in Authorization: Bearer headers.
  • Do not log credentials, tokens, authorization codes, or refresh tokens.
  • Use backend permissions, not frontend visibility, for admin enforcement.