Most session theft in a browser app is a script reading a token, a form posting from another site, or an authorization code landing on the wrong host. Authdog's defaults close the first two for the hosted cookie. You still own the headers on your own origin, and you still have to check webhook signatures.

## The session cookie

After sign-in, Authdog sets `authdog-session`:

- **`HttpOnly`**: page JavaScript cannot read it. An XSS bug on your origin can still act as the user while the cookie is sent, but it cannot exfiltrate the token to another host as easily.
- **`SameSite=Lax`**: the browser will not attach the cookie to cross-site POSTs. That is the CSRF control for cookie sessions.
- **`Secure` in production**: the cookie is not sent over plain HTTP.

A bearer token in `Authorization` does not get those protections. Anything that can read your client storage can replay it until expiry. Prefer the cookie for a first-party web app. Use the bearer for mobile, a SPA calling your API from a context that cannot use the cookie, or a service.

## Your app's headers

Authdog does not set a Content-Security-Policy on your origin. You do. A CSP that blocks unexpected scripts is what keeps an injected tag from running in the first place. The `HttpOnly` cookie is the backstop if that policy fails.

Start from this policy and add the hosts your app actually loads:

```
Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  connect-src 'self' https://*.authdog.com;
  frame-src https://*.authdog.com;
  img-src 'self' data: https://*.authdog.com;
  style-src 'self' 'unsafe-inline'
```

`connect-src` is the userinfo and token calls. `frame-src` is the hosted sign-in iframe, if you embed it. Drop `unsafe-inline` from `style-src` when your CSS does not need it. Add a CAPTCHA host here only if the challenge runs on your origin. Hosted Authdog pages send their own headers.

Also send your pages over HTTPS only. The `Secure` cookie flag does not upgrade a page you served as HTTP.

## Redirects

An OAuth redirect is a capability. If an attacker can choose the URI, they receive the code.

Redirect URIs are checked against your verified domains. The match is exact, including scheme, port, and trailing slash. `https://app.example.com/callback` does not match `https://app.example.com/callback/`. A mismatch fails as `redirect_uri_mismatch` before the user sees a sign-in form.

Set them per environment under **Redirects**. `dev` can point at localhost. `production` should not. See [Redirects](/docs/console/redirects).

## Hosted pages

**Trusted domain reminder** (**Authentication > Features**) shows "Only sign in if you recognize this address" with the identity host on hosted sign-in and sign-up. Turn it on when users might be phished toward a lookalike host. It does not scan the user's mail. It only labels the page they are actually on.

Custom domains serve those pages on your hostname. The reminder should show the host you expect users to recognize. See [Custom domains](/docs/custom-domains).

## Webhooks

Events you subscribe to are easy to forge if you trust the JSON alone. Authdog signs the body with HMAC in `X-Authdog-Signature: t=...,v1=...` and retries delivery.

Verify the signature and reject a timestamp that is too old before you create a user, revoke a seat, or send mail. The steps are in [Events and webhooks](/docs/events-webhooks).

## Related

| Read | To learn how to |
| --- | --- |
| [Session security](/docs/secure/session-security) | Lifetime, idle timeout, and revocation |
| [Redirects](/docs/console/redirects) | Callback, sign-out, and error URIs |
| [Events and webhooks](/docs/events-webhooks) | Verify `X-Authdog-Signature` |
| [Custom domains](/docs/custom-domains) | Serve hosted flows on your host |
