> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended**:
> ```bash
> npx @scalekit-inc/cli setup
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for 40+ agents.
> Skills: integrating-agentkit, implementing-saaskit, adding-mcp-oauth, implementing-modular-sso, implementing-scim-provisioning.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Express session middleware

Add hosted login and an encrypted session cookie to Express with ScalekitAuth
Use `ScalekitAuth` from `@scalekit-sdk/node/express` to add hosted login, an encrypted `sk_session` cookie, token refresh, and logout.

Typical flow: install `@scalekit-sdk/node` 2.12.0 or later, mount `auth.router`, and guard one route with `auth.requiresAuth`. Use the methods below to change paths, logout, or pass an existing [ScalekitClient](/saaskit/sdks/node/scalekit-client/).

Register these URLs in the Scalekit Dashboard under **Authentication > Redirects** before you test:

| Dashboard field | Must match |
| --- | --- |
| **Redirect URI** | `redirectUri` exactly, for example `http://localhost:5001/callback` |
| **Post Logout Redirect URI** | Absolute URL after full logout, for example `http://localhost:5001/` |
| **Initiate Login URL** | Login path, for example `http://localhost:5001/login` |

Store credentials in environment variables. Never hard-code secrets.

```bash title=".env" showLineNumbers=false
SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.com
SCALEKIT_CLIENT_ID=skc_...
SCALEKIT_CLIENT_SECRET=...
COOKIE_ENCRYPTION_SECRET=   # openssl rand -base64 32
REDIRECT_URI=http://localhost:5001/callback
```

Keep `COOKIE_ENCRYPTION_SECRET` identical on every server instance. The SDK does not ship a default.

## Install the package

```bash title="Terminal" showLineNumbers=false
npm install @scalekit-sdk/node express
```

`cookie-parser` is optional. `ScalekitAuth` reads the `Cookie` header when `req.cookies` is missing.

## Protect a route

```typescript title="server.ts" wrap showLineNumbers=false

const auth = new ScalekitAuth({
  envUrl: process.env.SCALEKIT_ENVIRONMENT_URL,
  clientId: process.env.SCALEKIT_CLIENT_ID,
  clientSecret: process.env.SCALEKIT_CLIENT_SECRET,
  redirectUri: process.env.REDIRECT_URI,
  cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET,
});

const app = express();
app.use(auth.router);

app.get('/account', auth.requiresAuth, (req, res) => {
  res.json({ sub: req.scalekitUser?.sub });
});

app.listen(5001);
```

Open `http://localhost:5001/account`. A missing session returns **302** to `/login?returnTo=/account`, not a JSON 401. After login, the callback restores `/account`.

`req.scalekitUser` is access-token claims. `sub` is always present. `email` appears only when you add it as a custom access-token claim.

> caution: Do not treat 302 as an API error
>
> A background `fetch` that follows redirects can complete a login instead of returning 401. Use `requiresAuth` on browser navigations, not as a JSON API gate.

<div class="sdk-client-page">

### constructor
<div class="sdk-method-section">
  
    

      Creates the Express session helper and builds `auth.router` for `/login`, `/callback`, and `/logout`.

      
        Existing client. When omitted, the constructor builds one from `envUrl`, `clientId`, and `clientSecret`.
      
      
        Scalekit environment URL.
      
      
        Application client ID.
      
      
        Application client secret.
      
      
        Exact **Redirect URI** registered in the dashboard.
      
      
        Secret used to encrypt `sk_session`. Generate with `openssl rand -base64 32`.
      
      
        Session cookie name.
      
      
        Path served by `auth.router` for login.
      
      
        Path served by `auth.router` for the OAuth callback.
      
      
        Path served by `auth.router` for logout.
      
      
        Fallback path after login when `returnTo` is absent.
      
      
        Where logout lands. Defaults to `postLoginRedirect`. Register the absolute URL as **Post Logout Redirect URI**.
      
      
        When `true`, logout ends the Scalekit session with `id_token_hint`. Set `false` to clear only the local cookie.
      
      
        Helper with `router` and `requiresAuth`.
      

```typescript wrap showLineNumbers=false
const auth = new ScalekitAuth({
  envUrl: process.env.SCALEKIT_ENVIRONMENT_URL,
  clientId: process.env.SCALEKIT_CLIENT_ID,
  clientSecret: process.env.SCALEKIT_CLIENT_SECRET,
  redirectUri: process.env.REDIRECT_URI,
  cookieEncryptionSecret: process.env.COOKIE_ENCRYPTION_SECRET,
});

app.use(auth.router);
```

    
  
</div>

### requiresAuth
<div class="sdk-method-section">
  
    
      

      Express middleware that requires a valid session. Refreshes the cookie about 10 seconds before expiry. Redirects to `loginPath` when the session is missing or invalid.

      
        Incoming request. On success, sets `req.scalekitUser` to access-token claims.
      
      
        Outgoing response. May receive a refreshed `sk_session` cookie.
      
      
        Called only when the session is valid.
      
      ">
        Completes the request, or sends 302 to `/login?returnTo=...`.
      

```typescript wrap showLineNumbers=false
app.get('/billing', auth.requiresAuth, (req, res) => {
  res.send(`Hello ${req.scalekitUser.sub}`);
});
```

    
  
</div>

### router
<div class="sdk-method-section">
  
    

      Express router that serves `loginPath`, `callbackPath`, and `logoutPath`. Mount it before protected routes.

      
        Router registered by the constructor.
      

```typescript wrap showLineNumbers=false
app.use(auth.router);
```

    
  
</div>

> note: Session cookies are Secure
>
> The helper sets the `Secure` attribute on `sk_session`. Use HTTPS, or a browser that accepts `Secure` cookies on `http://localhost`.

</div>


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
