---
title: Logout
---

AuthProxy exposes a well-known logout endpoint that ends the current session and returns the user to a
validated destination:

```
GET /.cratis/logout?redirect=<absolute-url>
```

The endpoint is anonymous — it works even when the session is already invalid — and is handled before
the authentication challenge stages, so it never bounces the user to a login provider.

---

## Full-chain logout

A plain cookie logout leaves the user signed in at the identity provider, so the next visit silently
re-authenticates. To avoid that, AuthProxy performs a **full-chain logout**: it ends the session both
locally *and* at the identity provider using OIDC [RP-initiated logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html).

When the session was established through an **OIDC provider**, a logout request:

1. Reads the stored `id_token` and the provider the session was established with from the authentication
   cookie, and clears the local session (see [What it clears](#what-it-clears)).
2. Redirects (`302 Found`) the browser to that provider's **end-session endpoint** (discovered from the
   provider's OpenID configuration) with:
   - `id_token_hint` — the stored `id_token`, so the provider knows which session to end.
   - `post_logout_redirect_uri` — AuthProxy's own callback, `/.cratis/logout/callback`.

   The validated final `redirect` target is carried across the round-trip in a short-lived, HTTP-only
   cookie (`.cratis-logout`) rather than in the URL.
3. The identity provider ends its own session and redirects back to `/.cratis/logout/callback`.
4. The callback clears every AuthProxy cookie again (idempotent) and redirects (`302 Found`) to the
   validated final `redirect` target.

> **Register the callback with each OIDC provider.** `post_logout_redirect_uri` must be allow-listed at
> the provider, so register `https://<your-proxy-host>/.cratis/logout/callback` as a permitted
> post-logout redirect URI for every OIDC application.

### OAuth 2.0 providers (e.g. GitHub)

OAuth 2.0 providers have no standard OIDC end-session endpoint and cannot be force-logged-out via a
redirect. When the session was established through an OAuth provider — or when there is no active OIDC
session, or the provider's discovery document advertises no end-session endpoint — AuthProxy falls back to
a **local-only logout**: it clears its own cookies and redirects straight to the validated `redirect`
target. The user's session at the OAuth provider is left untouched, so a later visit may still
re-authenticate silently without asking for credentials. This is a limitation of the OAuth providers, not
of AuthProxy.

---

## What it clears

Both the local logout and the post-logout callback:

1. Sign the user out of the authentication cookie (`.Cratis.AuthProxy.Auth.v2`), including any chunked variants.
2. Delete every AuthProxy session cookie:
   - `.cratis-identity`
   - `.cratis-identity-authorization`
   - `.cratis-tenant`
   - `.cratis-tenants`
   - `.cratis-invite`
   - `.cratis-invite-state`
   - `.cratis-registration`
   - `.cratis-providers`
3. Delete every transient sign-in handshake cookie the browser sent (`.AspNetCore.Correlation.*`,
   `.AspNetCore.OpenIdConnect.Nonce.*`) — the leftovers of abandoned handshakes that would otherwise
   poison the next sign-in. See [Failed Sign-ins](/authproxy/configuration/failed-sign-ins/#handshake-cookie-hygiene).
4. Delete any **additional cookies** the deployment configured under
   `Cratis:AuthProxy:Logout:AdditionalCookies` — cookies AuthProxy does not issue itself but that must
   not survive a logout. See [Clearing additional cookies](#clearing-additional-cookies).

The `.cratis-logout` carry cookie is deleted by the callback once the final target has been read from it.

After logout nothing of the session survives, so the next visit to a protected resource lands on the
provider-selection page (or the single provider's login) rather than silently reusing anything local.
Whether the *identity provider* still remembers the user is governed by the full-chain behavior above.

---

## The `redirect` parameter

The post-logout destination is supplied as an **absolute URL** in the `redirect` query-string parameter,
for example:

```
/.cratis/logout?redirect=https://cratis.studio
```

Because the target is absolute, it cannot be validated with the relative-URL check used elsewhere.
Instead it is matched against an **allow-list of origins** so neither the endpoint nor its callback can be
turned into an open redirect. The target is validated on both legs of the round-trip. A target is allowed
when its origin (scheme + host + port) matches any of:

- The proxy's **own public origin** as seen by the browser (derived from the request, honoring
  `X-Forwarded-Proto`). This covers redirecting back to the site the user is already on.
- Any configured **service frontend** (`Cratis:AuthProxy:Services:<name>:Frontend:BaseUrl`).
- The configured **lobby frontend** (`Cratis:AuthProxy:Invite:Lobby:Frontend:BaseUrl`).
- Any origin listed in **`Cratis:AuthProxy:Logout:AllowedRedirectOrigins`** (see below).

Same-site **relative** URLs (a single leading `/`, but not `//`) are always allowed.

If `redirect` is missing, empty, or fails validation, AuthProxy falls back to the application root (`/`).

---

## Allowing additional post-logout origins

The implicit origins above cover redirecting back to the app itself, but a deployment often wants to send
the user somewhere else after logout — for example a separate marketing or landing site that is neither
the proxy's own origin nor a configured frontend. List those origins under
`Cratis:AuthProxy:Logout:AllowedRedirectOrigins`. Each entry is an absolute origin (scheme + host,
optionally a port) with no path; malformed or non-HTTP(S) entries are ignored.

```json
{
  "Cratis": {
    "AuthProxy": {
      "Logout": {
        "AllowedRedirectOrigins": [
          "https://cratis.studio"
        ]
      }
    }
  }
}
```

Equivalent environment variables (one indexed key per entry):

```
Cratis__AuthProxy__Logout__AllowedRedirectOrigins__0=https://cratis.studio
```

With the example above, `GET /.cratis/logout?redirect=https://cratis.studio` is permitted even when the
app itself is served from a different host such as `https://app.cratis.studio`.

---

## Clearing additional cookies

Logout clears every cookie *AuthProxy* issues — but a deployment often runs sibling authentication
infrastructure whose cookies AuthProxy knows nothing about. The classic case is a separate
[oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/) guarding an admin app whose session cookie
was scoped to the **parent domain** (e.g. `_oauth2_proxy_admin` on `.cratis.studio`): every browser that
ever signed in there sends it on every `*.cratis.studio` request, and it survives AuthProxy's logout —
leaving the user half-signed-in from the application's point of view.

List such cookies under `Cratis:AuthProxy:Logout:AdditionalCookies` and the session-termination sweep
deletes them too. Each entry has:

- **`Name`** *(required)* — the exact cookie name. Matching is by exact name only; there is no prefix or
  wildcard support.
- **`Domain`** *(optional)* — the domain the cookie was scoped to, e.g. `.cratis.studio`. When set, the
  deletion is issued **for that domain as well as the request host** — necessary because a host-scoped
  deletion cannot touch a parent-domain cookie. Deleting for a parent domain of the current host is legal,
  which is exactly what makes this work. Leave it out for a cookie scoped to the request host itself.

Deletions are issued at the root path (`Path=/`) with the `Secure` attribute mirroring the request scheme,
so they reliably match how such cookies are typically written.

```json
{
  "Cratis": {
    "AuthProxy": {
      "Logout": {
        "AdditionalCookies": [
          {
            "Name": "_oauth2_proxy_admin",
            "Domain": ".cratis.studio"
          }
        ]
      }
    }
  }
}
```

Equivalent environment variables (one indexed entry per cookie):

```
Cratis__AuthProxy__Logout__AdditionalCookies__0__Name=_oauth2_proxy_admin
Cratis__AuthProxy__Logout__AdditionalCookies__0__Domain=.cratis.studio
```

The configured cookies are cleared everywhere the session is terminated:

- the logout endpoint (both legs of a full-chain logout), and
- the sign-in sweep that runs when a provider callback completes — so a **fresh sign-in also heals** a
  browser still carrying the stale cookie, without requiring the user to log out first.

This is a cleanup mechanism for cookies that linger after the owning deployment has been fixed (for
example, until an old parent-domain cookie expires) — it does not log the user out of the system that
issued the cookie.

---

## Example

A "Log out" control in the application simply navigates the browser to the endpoint:

```html
<a href="/cratis/logout/?redirect=https://cratis.studio">Log out</a>
```

For an OIDC session the browser is taken through the provider's end-session endpoint and back via the
callback; for an OAuth session it lands on the target directly. Either way the user ends up on the target
unauthenticated at AuthProxy, and requesting a protected resource then triggers the normal login flow.
