Skip to content
Talk to an Engineer Dashboard

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.

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)
UserDesktop / Mobile appSystem browserScalekit Click "Login" Open authorize URL Redirect to /oauth/authorize Redirect to callback URI with code + state Return control via deep link / loopback POST /oauth/token access_token, refresh_token, id_token Store tokens + continue
  1. 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=S256

    Native applications should generate and store:

    • state (to validate the callback)
    • code_verifier (kept locally), and the derived code_challenge (sent on /oauth/authorize)

    The authorization request is always opened in the system browser.

    For detailed definition of the base parameters, refer here

  2. 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/token
    Content-Type: application/x-www-form-urlencoded
    grant_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.

  3. 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/token
    Content-Type: application/x-www-form-urlencoded
    grant_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).
    • iss matches your Scalekit environment
    • aud matches your client_id
    • exp and iat are valid

    Public keys for verification are available at:

    Terminal window
    <ENV_URL>/keys
  4. 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.

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:

  1. Check if the error parameter exists in the callback URI
  2. Log the error and error_description for debugging
  3. Display a user-friendly message in your app
  4. Provide an option to retry login

Common error codes:

ErrorDescription
access_deniedUser denied the authorization request
invalid_requestMissing or invalid parameters (e.g., invalid PKCE challenge)
server_errorScalekit encountered an unexpected error

Native apps should use platform-specific secure storage mechanisms:

PlatformRecommended Storage
iOSKeychain Services
AndroidEncryptedSharedPreferences or Keystore
macOSKeychain
WindowsWindows Credential Manager or DPAPI
LinuxSecret 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