# Security (https://session-kit.pages.dev/docs/security)

Session kit saves the tokens your backend issues and manages the session
around them. This page explains what stays your backend's job, where tokens
live in each setup, and where the client sends them.

## Your backend decides what's allowed

Your backend must authenticate every request and authorize private
operations. Route guards, screen guards and the user's fields in the client
decide what to show, not what's allowed.

With server rendering, `signIn` seals what it's given. If the browser signs
in with your backend and sends the tokens and the user, the cookie's user is
what the browser sent, until a refresh or `updateUser` replaces it. Use it to
show who is signed in. Let your backend authorize requests with the access
token, or check the tokens before `signIn`.

## Where tokens live

Each setup saves the tokens in a different place:

| Setup                                      | Where the tokens live                                       |
| ------------------------------------------ | ----------------------------------------------------------- |
| Browser app                                | `localStorage` or `sessionStorage`, through `webStorage()`  |
| [Server rendering](https://session-kit.pages.dev/docs/server-rendering) | An encrypted `httpOnly` cookie                              |
| [Expo app](https://session-kit.pages.dev/docs/setups/expo)              | The iOS keychain, or encrypted with an Android Keystore key |

Each place protects the tokens differently:

- **In a browser app**, any JavaScript on your origin can read the tokens, as
  with any credential a browser saves. They never go in cookies, so the
  browser doesn't send them on its own.
- **With server rendering**, page scripts can't read the cookie. Only the
  access token reaches page scripts, and only with `clientCredentials`.
- **In an Expo app**, the `expo-secure-store` config plugin keeps the
  session out of Android backups. `secureStorage()` removes a session that
  an earlier install left in the iOS keychain.

## The session route

With server rendering, the browser's client signs out and gets the access
token through your session route, and reads the session there in most
setups. `sessionHandler` from `@monarcode/session-kit/server` answers it,
through each framework's `sessionRoute`.

It answers your own site's pages only, so another site can't sign the user
out or ask for the access token. It makes two checks, and answers 403 to a
request that fails one:

- A `Sec-Fetch-Site` header, when the request has one, must be
  `same-origin`. Browsers send it with requests to https and localhost sites.
- On plain http, where browsers don't send that header, a `POST` must have
  an `Origin` header that matches the request's URL. A `GET` passes: it
  changes nothing, and another site can't read the answer.

Signing in stays your own server code. A route of your own that changes the
session must refuse other sites too, where the framework doesn't check. Make
the same two checks there.

## Where tokens go

[`createAuthFetch`](https://session-kit.pages.dev/docs/client/requests) and the effetch plugin
[`sessionBearer`](https://session-kit.pages.dev/docs/effect#6-call-your-api-with-effetch) send the access
token only to your API's origin:

- A request to another origin, or with a username or password in its URL,
  fails with `INVALID_OPTIONS` before anything is sent.
- A redirect fails rather than carrying the token on.

[Debug events](https://session-kit.pages.dev/docs/configuration/debugging) leave out tokens unless you
turn on `sensitive`.

## Keep tokens out of page data

Sessions, the auth state, route context and the loader results Session kit
makes hold no tokens: a `Session` has the user and its `sessionId` only.
Don't put tokens in loader results or hydrated page data yourself.