Mobile & desktop applications
Implement Multi-App Authentication for mobile and desktop apps using Authorization Code with PKCE
Mobile and desktop applications are native clients. Like SPAs, they are public OAuth clients and do not use a client_secret. These applications initiate login, handle the callback, and perform the authorization code exchange using Authorization Code with PKCE.
This guide covers the authorization code flow with PKCE for mobile and desktop applications.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- A Scalekit account with an environment configured
- Your environment URL (
ENV_URL), e.g.,https://yourenv.scalekit.com - A native application registered in Scalekit with a
client_id(Create one →) - A callback URI configured:
- Mobile: Custom URI scheme (e.g.,
myapp://callback) or universal/app links - Desktop: Custom URI scheme or loopback address (e.g.,
http://127.0.0.1:PORT/callback)
- Mobile: Custom URI scheme (e.g.,
High-level flow
Section titled “High-level flow”Step-by-step implementation
Section titled “Step-by-step implementation”-
Initiate login or signup
Section titled “Initiate login or signup”Login begins when your application opens the system browser and redirects the user to Scalekit’s hosted login page.
Terminal window <ENV_URL>/oauth/authorize?response_type=code&client_id=<CLIENT_ID>&redirect_uri=<CALLBACK_URI>&scope=openid+profile+email+offline_access&state=<RANDOM_STATE>&code_challenge=<PKCE_CODE_CHALLENGE>&code_challenge_method=S256Native applications should generate and store:
state(to validate the callback)code_verifier(kept locally), and the derivedcode_challenge(sent on/oauth/authorize)
The authorization request is always opened in the system browser.
For detailed definition of the base parameters, refer here
-
Handle the callback and complete login
Section titled “Handle the callback and complete login”After authentication, Scalekit redirects the user back to your application using the registered callback mechanism.
Common patterns include:
- Mobile apps: custom URI schemes or universal links
- Desktop apps: custom URI schemes or a loopback HTTP server on
localhost
Your application must:
- Validate the returned
state - Handle any error parameters
- Exchange the authorization code for tokens using the stored
code_verifier
Terminal window POST <ENV_URL>/oauth/tokenContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&client_id=<CLIENT_ID>&code=<CODE>&redirect_uri=<CALLBACK_URI>&code_verifier=<PKCE_CODE_VERIFIER>{"access_token": "...","refresh_token": "...","id_token": "...","expires_in": 299}Note: Authorization codes are single-use and short-lived.
-
Manage sessions and token refresh
Section titled “Manage sessions and token refresh”Once tokens are issued, your native application is responsible for session management.
Token roles
- Access token: short-lived, used for authenticated requests
- Refresh token: used to obtain new tokens
- ID token: user identity claims and required for logout
Tokens should be stored using secure, OS-backed storage appropriate for the platform.
When an access token expires, request new tokens using the refresh token:
Terminal window POST <ENV_URL>/oauth/tokenContent-Type: application/x-www-form-urlencodedgrant_type=refresh_token&client_id=<CLIENT_ID>&refresh_token=<REFRESH_TOKEN>Validate access tokens by checking:
- Verify the token signature using Scalekit’s public keys (JWKS).
issmatches your Scalekit environmentaudmatches yourclient_idexpandiatare valid
Public keys for verification are available at:
Terminal window <ENV_URL>/keys -
Implement logout
Section titled “Implement logout”Logging out requires clearing your local session and invalidating the Scalekit session.
Your logout action should:
- Extract the ID token (if available)
- Clear locally stored tokens
- Redirect the system browser to Scalekit’s logout endpoint
Terminal window <ENV_URL>/oidc/logout?id_token_hint=<ID_TOKEN>&post_logout_redirect_uri=<POST_LOGOUT_REDIRECT_URI>Logout must be performed via a browser redirect so that Scalekit can identify and terminate the session.
Handle errors
Section titled “Handle errors”When authentication fails, Scalekit redirects to your callback URI with error parameters instead of an authorization code:
myapp://callback?error=access_denied&error_description=User+denied+access&state=<STATE>Your callback handler should check for errors before processing the authorization code:
- Check if the
errorparameter exists in the callback URI - Log the
erroranderror_descriptionfor debugging - Display a user-friendly message in your app
- Provide an option to retry login
Common error codes:
| Error | Description |
|---|---|
access_denied | User denied the authorization request |
invalid_request | Missing or invalid parameters (e.g., invalid PKCE challenge) |
server_error | Scalekit encountered an unexpected error |
Token storage security
Section titled “Token storage security”Native apps should use platform-specific secure storage mechanisms:
| Platform | Recommended Storage |
|---|---|
| iOS | Keychain Services |
| Android | EncryptedSharedPreferences or Keystore |
| macOS | Keychain |
| Windows | Windows Credential Manager or DPAPI |
| Linux | Secret Service API (libsecret) |
Recommendations:
- Never store tokens in plain text files or unencrypted databases
- Use biometric or device PIN protection for sensitive token access when available
- Clear tokens from secure storage on logout
What’s next
Section titled “What’s next”- Set up a custom domain for your authentication pages
- Add enterprise SSO to support SAML and OIDC with your customers’ identity providers