Mobile & desktop applications
Implement Multi-App Authentication for mobile and desktop apps using Authorization Code with PKCE
Implement login, token management, and logout in your mobile or desktop application using Authorization Code with PKCE. Native apps are public OAuth clients that cannot securely store a client_secret in the application binary, so they use PKCE to protect the authorization flow. This guide covers initiating login through the system browser, handling deep link callbacks, managing tokens in secure storage, and implementing logout.
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”Initiate login by opening the system browser with the authorization URL. Always use the system browser rather than an embedded WebView — this lets users leverage existing sessions and provides a familiar, secure authentication experience.
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=S256Generate and store these values before opening the browser:
state— Validate this on callback to prevent CSRF attackscode_verifier— A cryptographically random string you keep in the appcode_challenge— Derived from the verifier using S256 hashing; send this in the authorization URL
For detailed parameter definitions, see Initiate signup/login.
-
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 callback patterns:
- Mobile apps — Custom URI schemes (e.g.,
myapp://callback) or universal links (iOS) / app links (Android) - Desktop apps — Custom URI schemes or a temporary HTTP server on localhost
Your callback handler must:
- Validate the returned
statematches what you stored — this confirms the response is for your original request - Handle any error parameters before processing
- Exchange the authorization code for tokens by including the
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} - Mobile apps — Custom URI schemes (e.g.,
-
Manage sessions and token refresh
Section titled “Manage sessions and token refresh”Store tokens in platform-specific secure storage and validate them on each request. When access tokens expire, use the refresh token to obtain new ones without requiring the user to re-authenticate.
Token roles
- Access token — Short-lived token (default 5 minutes) for authenticated API requests
- Refresh token — Long-lived token to obtain new access tokens
- ID token — JWT containing user identity claims; required for logout
Store tokens using secure, OS-backed storage appropriate for each platform. See Token storage security for platform-specific recommendations.
When an access token expires, request new tokens:
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 verifying:
- Token signature using Scalekit’s public keys (JWKS endpoint)
issmatches your Scalekit environment URLaudincludes yourclient_idexpandiatare valid timestamps
Public keys for signature verification:
Terminal window <ENV_URL>/keys -
Implement logout
Section titled “Implement logout”Clear your local session and redirect the system browser to Scalekit’s logout endpoint to invalidate the shared session.
Your logout action must:
- Extract the ID token before clearing local storage
- Clear tokens from secure storage
- Open 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>
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>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 have access to platform-specific secure storage mechanisms that encrypt tokens at rest and protect them from other applications. Unlike browser storage, these mechanisms provide strong protection against token theft from device compromise or malware.
Use platform-specific secure storage for each platform:
| 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, shared preferences, or unencrypted databases — these can be read by any application with storage access
- Use biometric or device PIN protection for sensitive token access when available — this adds a second factor for token access
- Clear tokens from secure storage on logout — this ensures a clean state for the next authentication
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