-
Notifications
You must be signed in to change notification settings - Fork 93
docs: Add web authentication with httpOnly cookies #761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
FXschwartz
wants to merge
4
commits into
serverpod:main
Choose a base branch
from
FXschwartz:4045-web-auth-docs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
8701073
Add web authentication with httpOnly cookies documentation.
FXschwartz 9e19865
docs: Address review on the web authentication page
developerjamiu e64d5cb
docs: Verify the auth cookie via the network tab
developerjamiu 440b25d
docs: State what each token manager delivers in the sign-in body
developerjamiu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
docs/06-concepts/04-authentication/10-web-authentication.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| --- | ||
| sidebar_label: Web setup | ||
| description: Keep web sign-in tokens in httpOnly cookies so JavaScript can never read them, with server configuration, client setup, CORS implications, and local development notes. | ||
| --- | ||
|
|
||
| # Set up authentication on the web | ||
|
|
||
| Browsers have no secure storage: anything kept in `localStorage`, `sessionStorage`, or IndexedDB is readable by any JavaScript running on the page, so a single XSS vulnerability can steal a signed-in user's token and replay it from anywhere. Serverpod's cookie mode keeps web tokens in `httpOnly` cookies instead, which scripts cannot read. Cookie mode is opt-in and recommended for any app with signed-in users on the web. | ||
|
|
||
| Native and desktop apps are unaffected: they keep their tokens in secure OS storage (such as the Keychain) and need no changes. | ||
|
|
||
| ## Before you start | ||
|
|
||
| - A Serverpod project with the [authentication module](./setup) enabled. | ||
| - Your app served over `https` in production. Auth cookies are marked `Secure` by default; relax this only for `http://localhost` during development. | ||
|
|
||
| ## Configure the server | ||
|
|
||
| Enable cookie auth by adding an `authCookie` section to your server configuration (`config/development.yaml`, `config/production.yaml`, and so on), together with the list of origins your web app is served from: | ||
|
|
||
| ```yaml | ||
| authCookie: | ||
| # secure: false # Uncomment only for http://localhost development. | ||
| allowedOrigins: | ||
| - https://app.example.com | ||
| ``` | ||
|
|
||
| `allowedOrigins` is required when `authCookie` is set: it backs the CSRF origin checks and credentialed CORS, which cannot use a wildcard origin. List every browser origin that calls your server. With cookie auth enabled, browsers on origins that are not in the list lose cross-origin access, including to public endpoints. | ||
|
|
||
| All `authCookie` fields are optional: | ||
|
|
||
| | Field | Default | Purpose | | ||
| | ------------- | ------------------------ | -------------------------------------------------------------- | | ||
| | `name` | `serverpod_auth` | Name of the auth cookie. | | ||
| | `refreshName` | `<name>_refresh` | Name of the JWT refresh cookie. | | ||
| | `domain` | host-only | Set to `example.com` to share the cookie across subdomains. | | ||
| | `path` | `/` | Cookie path; also the base path behind a reverse proxy. | | ||
| | `secure` | `true` | Set to `false` only for `http://localhost` development. | | ||
| | `sameSite` | `lax` | `lax`, `strict`, or `none` (`none` requires `secure`). | | ||
|
|
||
| Each field can also be set through environment variables (`SERVERPOD_AUTH_COOKIE_NAME`, `SERVERPOD_AUTH_COOKIE_REFRESH_NAME`, `SERVERPOD_AUTH_COOKIE_DOMAIN`, `SERVERPOD_AUTH_COOKIE_PATH`, `SERVERPOD_AUTH_COOKIE_SECURE`, `SERVERPOD_AUTH_COOKIE_SAME_SITE`, and `SERVERPOD_ALLOWED_ORIGINS`), which override the YAML values. | ||
|
|
||
| ## Configure the client | ||
|
|
||
| Turn on cookie transport when the app runs on the web, immediately after constructing the client and before making any calls: | ||
|
|
||
| ```dart | ||
| import 'package:flutter/foundation.dart'; | ||
|
|
||
| client = Client(serverUrl) | ||
| ..cookieAuth = kIsWeb | ||
| ..connectivityMonitor = FlutterConnectivityMonitor() | ||
| ..authSessionManager = FlutterAuthSessionManager(); | ||
| ``` | ||
|
|
||
| Everything else is unchanged: sign-in flows, the `client.auth` session manager, and endpoint calls work as on other platforms. Setting `cookieAuth` to `true` on a non-web platform throws, since those transports have no browser cookie jar. | ||
|
|
||
| ## Verify | ||
|
|
||
| 1. Sign in from your web app with the browser's developer tools open. | ||
| 2. In the **Network** tab, inspect the sign-in response. It carries an `HttpOnly` `Set-Cookie` header: `serverpod_auth` holding the session token with server-side sessions, or `serverpod_auth_refresh` holding the refresh token with JWT. The cookie's token no longer appears in the response body. With JWT, the body still carries the short-lived access token, which the app keeps in memory. | ||
| 3. Confirm no token appears in **Local Storage** for your app's origin. | ||
| 4. Reload the page. The user is still signed in. | ||
|
|
||
| If sign-in fails, check that every browser origin is listed in `allowedOrigins`, and on `http://localhost` that `authCookie.secure` is `false`. | ||
|
|
||
| ## How it works | ||
|
|
||
| - With **server-side sessions**, the session token is delivered as an `httpOnly` cookie and never appears in the response body. | ||
| - With **JWT**, the access token is kept in memory only, and the refresh token is delivered as an `httpOnly` cookie scoped to the refresh endpoint's path. On page load, the session is restored by refreshing from the cookie. Multiple tabs coordinate their refreshes through the browser's Web Locks API, so a shared refresh token is only rotated by one tab at a time. | ||
| - **Signing out** clears the cookies and revokes the session on the server. | ||
| - **Method streams** authenticate from the cookie at the WebSocket handshake. When the signed-in user changes (sign-in or sign-out), open method streams are closed gracefully (subscriptions receive `onDone` without an error) and new streams connect with the current identity. This applies on every platform, not only the web. | ||
| - **Switching users requires a sign-out first.** Signing in as a different user from an already-authenticated session is rejected with a `SignInWhileAuthenticatedException` on all platforms. | ||
|
|
||
| ## Cross-site request protection | ||
|
|
||
| Cookie auth is protected against CSRF in layers: cookies default to `SameSite=Lax`, the server validates the request `Origin` against `allowedOrigins`, and the cookie only authenticates requests carrying a marker header set by the client, which a cross-site form cannot add without a CORS preflight. Nothing needs configuring beyond `allowedOrigins`. Set `sameSite: none` only if your app is embedded cross-site, and keep `secure: true` with it. | ||
|
|
||
| ## Cross-subdomain cookies | ||
|
|
||
| To share the signed-in session between `app.example.com` and other subdomains, set `authCookie.domain` to the registrable domain: | ||
|
|
||
| ```yaml | ||
| authCookie: | ||
| domain: example.com | ||
| ``` | ||
|
|
||
| By default the cookie is host-only. | ||
|
|
||
| ## Local development | ||
|
|
||
| - On `http://localhost`, set `authCookie.secure: false` so the browser accepts the cookies. | ||
| - On a plain-`http` LAN address (testing from another device), browsers disable the Web Locks API outside secure contexts, so cross-tab refresh coordination is off. The client logs a warning, and refreshing in several tabs at the same moment can occasionally sign the user out. This is an artifact of the insecure test origin; production `https` deployments are unaffected. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Setup](./setup): install and configure the authentication module. | ||
| - [Token managers](./token-managers/managing-tokens): choose between JWT and server-side sessions. | ||
| - [Streaming](../endpoints-and-apis/streaming): how method streams work. | ||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.