
# What your app receives after sign-in

When a visitor signs in through an **OAuth2** middleware, including **Sign in with Cruma**, the agent forwards their request to your app with the visitor's identity attached. Your app reads the identity from request headers and never handles the sign-in itself. The same facts are shown under **What your app receives** on the **Auth** page of the desktop app, the terminal interface and the web dashboard.

## Headers

| Header | Example | Meaning |
|---|---|---|
| `x-auth-subject` | `1234567890` | Who signed in: the user id. |
| `x-auth-provider` | `cruma` | Which sign-in they used. |
| `x-auth-claims` | `eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJBZGEifQ` | Base64url JSON of the provider's claims, such as email and name. |
| `x-cruma-identity` | `eyJhbGciOiJFZERTQSJ9.…` | The same identity as a short-lived token signed by the agent. |

`x-auth-subject` is on every signed-in request. `x-auth-provider` and `x-auth-claims` are added when the sign-in carries them. Requests that are not signed in carry none of these headers.

## Signing a visitor out

Link the visitor to the middleware's sign-out path with a plain `GET`. The agent clears the session and returns them to `/`, or to the relative path in `?next=`. A `next` that points at another site is ignored.

| Middleware created in | Sign-out path |
|---|---|
| The desktop app, the terminal interface or the web dashboard | `/_auth/logout` |
| A coding agent over MCP, for a shared site | `/_cruma_auth/logout` |

The path is the middleware's `logout_path` setting, so a configuration file can use another. The **Auth** page shows the path of the middleware you have.

```html
<a href="/_auth/logout?next=/goodbye">Sign out</a>
```

## When to trust the headers

The agent removes every copy of these headers that a visitor sends, on every route. Trust `x-auth-subject`, `x-auth-provider` and `x-auth-claims` only if your app can be reached solely through the agent: they were set by the agent, or they are absent. If your app can also be reached another way, such as directly on your network, do not trust them. `x-cruma-identity` is signed, so verify it instead where the agent sends it.

> **🚧 In development**
>
> Publishing the public key that verifies `x-cruma-identity` is not available yet. Until it is, rely on the `x-auth-*` headers behind the agent.

## Reading the headers

A Node.js app using Express:

```js
import express from "express";

const app = express();

app.get("/", (req, res) => {
  const subject = req.get("x-auth-subject");
  if (!subject) return res.status(401).send("Not signed in");

  const claims = JSON.parse(
    Buffer.from(req.get("x-auth-claims") ?? "e30", "base64url").toString(),
  );
  res.send(`Hello ${claims.name ?? subject}. <a href="/_auth/logout">Sign out</a>`);
});

app.listen(3000);
```

See [Security](https://cruma.io/book/cruma-tunnels/security.md) for the sign-in types and [Desktop security](https://cruma.io/book/cruma-tunnels/desktop-security.md) for the **Auth Providers** page.

---

- Book: [Cruma Tunnels](https://cruma.io/book/cruma-tunnels.md)
- Previous: [Project files](https://cruma.io/book/cruma-tunnels/project-files.md)
- Pages: [Home](https://cruma.io/home.md) · [Docs](https://cruma.io/docs.md) · [Pricing](https://cruma.io/pricing.md) · [Share localhost](https://cruma.io/share-localhost.md) · [Webhook testing](https://cruma.io/webhook-testing.md) · [Homelab](https://cruma.io/homelab.md) · [AI coding agents](https://cruma.io/ai-coding-agents.md) · [Cruma vs ngrok](https://cruma.io/ngrok-alternative.md) · [Cruma vs Cloudflare Tunnel](https://cruma.io/cloudflare-tunnel-alternative.md) · [Blog](https://cruma.io/blog.md)
