Single page application
Implement Multi-App Authentication for single page apps using Authorization Code with PKCE
Implement login, token management, and logout in your single page application (SPA) using Authorization Code with PKCE. SPAs run entirely in the browser and cannot securely store a client_secret, so they use PKCE (Proof Key for Code Exchange) to protect the authorization flow. This guide covers initiating login from your SPA, exchanging authorization codes for tokens, managing sessions, 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 SPA registered in Scalekit with a
client_id(Create one) - At least one redirect URL configured in Dashboard > Developers > Applications > [Your App] > Redirects
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 redirecting the user to Scalekit’s hosted login page. Include the PKCE code challenge in the authorization request to protect against authorization code interception attacks.
Terminal window <ENV_URL>/oauth/authorize?response_type=code&client_id=<CLIENT_ID>&redirect_uri=<CALLBACK_URL>&scope=openid+profile+email+offline_access&state=<RANDOM_STATE>&code_challenge=<PKCE_CODE_CHALLENGE>&code_challenge_method=S256Generate and store these values before redirecting:
state— Validate this on callback to prevent CSRF attackscode_verifier— A cryptographically random string you keep locallycode_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 callback URL with an authorization
codeand thestateyou sent.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_URL>&code_verifier=<PKCE_CODE_VERIFIER>{"access_token": "...","refresh_token": "...","id_token": "...","expires_in": 299} - Validate the returned
-
Manage sessions and token refresh
Section titled “Manage sessions and token refresh”Store tokens and validate them on each request. When access tokens expire, use the refresh token to obtain new ones without requiring the user to authenticate again.
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 client-side based on your security requirements. See Token storage security for guidance on choosing the right storage mechanism.
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 to Scalekit’s logout endpoint to invalidate the shared session.
Your logout action must:
- Extract the ID token before clearing local storage
- Clear locally stored tokens from memory or storage
- Redirect the 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 URL with error parameters instead of an authorization code:
/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 URL - Log the
erroranderror_descriptionfor debugging - Display a user-friendly message
- 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”SPAs run entirely in the browser where tokens are vulnerable to cross-site scripting (XSS) attacks. An attacker who successfully injects malicious JavaScript can read tokens from any accessible storage and use them to impersonate the user.
Choose a storage strategy based on your security requirements:
| Storage | Security | Trade-off |
|---|---|---|
| Memory (JavaScript variable) | Most secure — not accessible to XSS | Tokens lost on page refresh; requires silent refresh |
| Session storage | Moderate — cleared when tab closes | Accessible to XSS; persists during session |
| Local storage | Least secure — persists across sessions | Accessible to XSS; long exposure window |
Recommendations:
- For high-security applications, store tokens in memory and use silent refresh (iframe-based token renewal) to maintain sessions across page loads
- Always sanitize user inputs and use Content Security Policy (CSP) headers to mitigate XSS attacks
- Never log tokens or include them in error messages
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