# Web application

Implement login, token management, and logout in your web application using the Authorization Code flow. Web applications have a backend server that can securely store a `client_secret`, allowing them to authenticate directly with Scalekit's token endpoint. This guide covers initiating login from your backend, exchanging authorization codes for tokens, managing sessions with secure cookies, and implementing logout.

:::tip
[**Check out the example apps on GitHub**](https://github.com/scalekit-inc/multiapp-demo) to see Web, SPA, Desktop, and Mobile apps sharing a single Scalekit session.
:::

## 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 web application registered in Scalekit with `client_id` and `client_secret` ([Create one](/authenticate/fsa/multiapp/manage-apps))
- At least one redirect URL configured in **Dashboard > Developers > Applications > [Your App] > Redirects**

## High-level flow

```d2
shape: sequence_diagram

User
"Web app (frontend)"
"Web app (backend)"
Scalekit

User -> "Web app (frontend)": Click "Login"
"Web app (frontend)" -> "Web app (backend)": GET /login
"Web app (backend)" -> Scalekit: Redirect to /oauth/authorize
Scalekit -> "Web app (backend)": Redirect to /callback with code + state
"Web app (backend)" -> Scalekit: POST /oauth/token
Scalekit -> "Web app (backend)": access_token, refresh_token, id_token
"Web app (backend)" -> "Web app (frontend)": Set session cookies + redirect
```

## Step-by-step implementation

1. ## Initiate login or signup

   Initiate login by redirecting the user to Scalekit's hosted login page from your backend. Generate and store a `state` parameter before redirecting to validate the callback.

   ```sh
   <ENV_URL>/oauth/authorize?
     response_type=code&
     client_id=<CLIENT_ID>&
     redirect_uri=<CALLBACK_URL>&
     scope=openid+profile+email+offline_access&
     state=<RANDOM_STATE>
   ```
**Why web apps use client_secret:** Web applications store the `client_secret` on the backend server where it cannot be accessed by browsers or end users. This allows your backend to authenticate directly with Scalekit's token endpoint without needing PKCE. Never expose the `client_secret` to the frontend or include it in client-side JavaScript.

   For detailed parameter definitions, see [Initiate signup/login](/authenticate/fsa/implement-login).

2. ## Handle the callback and complete login

   After authentication, Scalekit redirects the user back to your callback endpoint with an authorization `code` and the `state` you sent.

   Your backend must:

   - Validate the returned `state` matches what you stored — this confirms the response is for your original request and prevents CSRF attacks
   - Handle any error parameters before processing
   - Exchange the authorization code for tokens using your `client_secret`

   ```sh
   POST <ENV_URL>/oauth/token
   Content-Type: application/x-www-form-urlencoded

   grant_type=authorization_code&
   client_id=<CLIENT_ID>&
   client_secret=<CLIENT_SECRET>&
   code=<CODE>&
   redirect_uri=<CALLBACK_URL>
   ```

   ```json
   {
     "access_token": "...",
     "refresh_token": "...",
     "id_token": "...",
     "expires_in": 299
   }
   ```
**Authorization codes expire after one use:** Authorization codes are single-use and expire quickly (approximately 10 minutes). If you attempt to reuse a code or it expires, start a new login flow to obtain a fresh authorization code.

3. ## Manage sessions and token refresh

   Store tokens in secure cookies and validate the access token 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 in secure, HttpOnly cookies with appropriate path scoping to limit exposure.

   When an access token expires, request new tokens:

   ```sh
   POST <ENV_URL>/oauth/token
   Content-Type: application/x-www-form-urlencoded

   grant_type=refresh_token&
   client_id=<CLIENT_ID>&
   client_secret=<CLIENT_SECRET>&
   refresh_token=<REFRESH_TOKEN>
   ```

   Validate access tokens by verifying:

   - Token signature using Scalekit's public keys (JWKS endpoint)
   - `iss` matches your Scalekit environment URL
   - `aud` includes your `client_id`
   - `exp` and `iat` are valid timestamps

   Public keys for signature verification:

   ```sh
   <ENV_URL>/keys
   ```

4. ## Implement logout

   Clear your application session and redirect to Scalekit's logout endpoint to invalidate the shared session.

   Your logout endpoint must:

   - Extract the ID token before clearing cookies
   - Clear application session cookies
   - Redirect the browser to Scalekit's logout endpoint

   ```sh
   <ENV_URL>/oidc/logout?
     id_token_hint=<ID_TOKEN>&
     post_logout_redirect_uri=<POST_LOGOUT_REDIRECT_URI>
   ```
**Logout must be a browser redirect:** Use a browser redirect to the `/oidc/logout` endpoint, not an API call. The redirect ensures Scalekit's session cookie is sent with the request, allowing Scalekit to identify and terminate the correct session.

   Configure [backchannel logout](/guides/dashboard/redirects/#back-channel-logout-url) URLs to receive notifications when a logout is performed from another application sharing the same user session.

## Handle errors

When authentication fails, Scalekit redirects to your callback URL with error parameters instead of an authorization code:

```sh showLineNumbers=false wrap
/callback?error=access_denied&error_description=User+denied+access&state=<STATE>
```

Check for errors before processing the authorization code:

- Check if the `error` parameter exists in the URL
- Log the `error` and `error_description` for 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 |
| `server_error` | Scalekit encountered an unexpected error |

## (Optional) Use Scalekit Management APIs

In addition to handling user authentication, web applications can call Scalekit's Management APIs from the backend. These APIs allow your application to interact with Scalekit-managed resources such as users, organizations, memberships, and roles.

Typical use cases include:

- Fetching the currently authenticated user
- Listing organizations the user belongs to
- Managing organization membership or roles

Management APIs are Scalekit-owned APIs intended for server-side use only. Enable Management API access in your application:

1. Go to **app.scalekit.com**
2. Navigate to **Developers > Applications**
3. Select your **Web Application**
4. Enable **Allow Scalekit Management API Access**
**Management API access is only available for web applications:** This option is only available for web applications because they can securely store credentials. When enabled, your backend can authenticate to Scalekit's Management APIs using the application's credentials. These calls are independent of end-user access tokens and are designed for trusted, server-side workflows.

## What's next

- [Configure backchannel logout](/guides/dashboard/redirects/#back-channel-logout-url) to receive notifications when a user logs out from another app
- [Set up a custom domain](/guides/custom-domain) for your authentication pages
- [Add enterprise SSO](/authenticate/auth-methods/enterprise-sso/) to support SAML and OIDC with your customers' identity providers