---
id: security-model
title: Cookie-based security model
sidebar_label: Cookie-based security
---

Ory Identities supports both mobile (native) and browser applications. Because of the broad capabilities browsers offer, they pose
a higher security risk than native applications. To shield your users from those risks, Ory Identities implements special browser
APIs which use additional security measures such as anti-CSRF cookies.

Although it is an uncommon approach that might require a shift in thinking, it's implemented so that you don't have to worry about
refreshing tokens or deciding whether to store them in `localStorage` or `document.cookies`.

Instead, you can devote all your focus and time to developing great software for your users, while Ory takes care of the security
by giving you the best-in-class protection from all common browser attack vectors, such as
[Cross-site scripting (XSS)](https://owasp.org/www-community/attacks/xss/) or
[Cross-site request forgery (CSRF)](https://owasp.org/www-community/attacks/csrf/).

## Access to your domain

To manage HTTP cookies, Ory APIs must be exposed on the same domain as your application. If you don't fulfill this requirement,
HTTP Cookies are ignored by the browser, which prevents the Ory Identities from functioning properly.

Ory exposes the APIs at `https://$PROJECT_SLUG.projects.oryapis.com`. To manage session information, Ory Identities must be able
to set the domain in HTTP Cookies to the same domain as the application that consumes its APIs. For example:

- When working with an application that runs on `http://localhost:3000` for local development, Ory must be able to set
  `domain=localhost` in the HTTP cookie.

{/* TODO: add document describing deployment on vercel */} Ory offers SDKs for certain deployment options such as
[Vercel](https://vercel.com) which mirror Ory's APIs without the need of running another process.

- When working with an application that runs on `https://app.example.org`, Ory must be able to set `domain=example.org` in the
  HTTP cookie.
- Some multi-tenant providers like Heroku (`<your-slug>.herokuapp.com`) or Vercel (`<your-slug>.vercel.app`) expose many apps
  under the same domain. These domains are listed in
  [Public Suffix Domain List](https://publicsuffix.org/list/public_suffix_list.dat). For these domains it is not possible to set a
  cookie on the root domain (`cookie=herokuapp.com`) but only on the subdomain (`cookie=your-slug.herokuapp.com`).

### What about local development?

When developing locally, your application typically either runs on `localhost` or on a local custom domain such as `app.local`.
However, your Ory Network project runs on your slug URL `https://$PROJECT_SLUG.projects.oryapis.com`. The Ory APIs can't set
cookies on a different domain than the one they are running on.

The solution is to tunnel the Ory APIs on a port on your local machine, which allows cookies to be set on the same domain as the
application is running.

Read more in the [Ory Tunnel documentation](./guides/cli/20_proxy-and-tunnel.mdx#ory-tunnel).

:::note

Ory Tunnel should be used for development only. Do not use Ory Tunnel in a production environment!

:::

## HTTP cookies

HTTP cookies are a central part of the unique security model in Ory.

Whenever the client that consumes Ory APIs is a browser, the system uses HTTP cookies to store session states and protect against
attack vectors such as [CSRF](https://owasp.org/www-community/attacks/csrf).

Ory issues HTTP cookies with the following flags for the highest level of security:

- `secure`: The cookie is only sent over HTTPS connection to protect against man-in-the-middle attacks.
- `httpOnly`: The cookie is not available to JavaScript code to protect against
  [XSS](https://owasp.org/www-community/attacks/xss/).
- `sameSite=Strict`: The cookie can only be requested from the same origin to protect against CSRF attacks.

### HTTP cookie domains

When the server sets a cookie, it defines the `domain` the cookie is valid for:

```
Set-Cookie: <name>=<value>; domain=<domain>
```

At Ory, this value defaults to the domain of your project (`<project>.projects.oryapis.com`). The browser only accepts cookies
from the same domain.

If you make a request to `https://www.my-evil-app.com` and the server responds with

```
Set-Cookie: google_session=1234; domain=google.com
```

the browser rejects the cookie. The same happens when you make a request in the browser. The browsers sends cookies only to a
matching domain. When the browser makes a request to `https://www.my-evil-app.com`, it never sends cookies set for `google.com`.

How does a browser decide whether to accept or reject a cookie?

- A cookie set with `Set-Cookie: <name>=<value>; domain=example.org` will be sent in requests to `example.org` and all subdomains
  of `example.org` (for example`www.example.org`, `api.example.org`).
- A cookie set with `Set-Cookie: <name>=<value>; domain=api.example.org` will be sent to `api.example.org` and its subdomains (for
  example `service.api.example.org`) but not to `example.org` or `not-api.example.org`.
- Cookies ignore the port number, which is important in local development. A cookie set with
  `Set-Cookie: <name>=<value>; domain=example.org:1234` will be sent to `https://example.org:443`, `http://example.org:80`, and
  `http://api.example.org:1234`.

### Cross-Origin HTTP cookies

When working with Single Page Apps (SPA), you often fetch data from servers using AJAX requests. There are two types of
cross-origin AJAX requests:

1. **On the same top-level domain**: the browser address bar is `https://www.example.org` and the AJAX request goes to
   `https://api.example.org/api/...`.
2. **Across different top-level domains**: the browser address bar is `https://www.example.org` and the AJAX request goes to
   `https://api-example.com/...`.

What requirements must be met for these requests to work?

### Same top-level domain

These requests are allowed only if the server at `api.example.org`:

- responds with the appropriate [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers
- the JavaScript XHR request is made with `credentials: 'include'`

Setting `withCredentials` to `true` can be done in the Ory JavaScript / TypeScript SDK:

```js
import { FrontendApi, Configuration } from "@ory/client"

const ory = new FrontendApi(
  new Configuration({
    basePath,
    baseOptions: {
      // Ensures we send cookies in the CORS requests.
      withCredentials: true,
    },
  }),
)
```

or using the [Browser's Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API):

```js
fetch("https://ory.your-custom-domain.com/", {
  credentials: "include",
})
```

### Cross top-level domain

Sending cookies across different top-level domains is a practice that gets used less frequently nowadays to improve data privacy.

This practice was abused for user tracking and targeted advertising. Some browsers deprecated cross-domain cookies, while others
are planning to do so in the near future.

Notable mentions are:

1. [Safari](https://www.infoq.com/news/2020/04/safari-third-party-cookies-block/)
2. [Chrome](https://www.adexchanger.com/privacy/google-will-limit-cross-site-tracking-in-chrome-by-default-starting-in-february/)
3. [Firefox](https://www.theverge.com/2019/6/4/18650363/firefox-block-trackers-default-enhanced-tracking-protection)

## What about JSON Web Tokens?

To learn how Ory Identities supports JSON Web Tokens (JWTs) to manage sessions read the
[Session mangagement documentation](./kratos/session-management/01_overview.mdx).

To learn how to use Session to JWT, read the [Session to JWT documentation](./identities/session-to-jwt-cors.mdx).

## Can I use OAuth 2.0 / OpenID Connect?

Ory is fully compliant with OAuth 2.0 and OpenID Connect. If you are interested to use OAuth 2.0 / OpenID Connect for advanced use
cases, check out [Ory OAuth 2.0 and OpenID](https://www.ory.com/hydra) documentation.

:::tip

[You probably do not need OAuth2 / OpenID Connect](https://www.ory.com/oauth2-openid-connect-do-you-need-use-cases-examples)
covers common misconceptions about OAuth2 and OpenID Connect and is worth a read if you are unsure whether OAuth2 is the right fit
for you.

:::

At Ory, we believe that OAuth 2.0 and OpenID Connect isn't a one-size-fits-all solution. In fact, we think that you probably don't
need to use such complicated protocols at all! We recommend using [Ory OAuth2 & OpenID](https://www.ory.com/hydra) for targeted
use cases only, such as providing third-party integration with your application (for example, in the form of the familiar "Sign in
with [PROVIDER_NAME]" button).

## What about access tokens / refresh tokens?

You can generate access and refresh tokens using [Ory OAuth2 & OpenID](https://www.ory.com/hydra). We do not recommend using
access and refresh tokens for session management! Visit
[ Why you probably do not need OAuth2 / OpenID Connect](https://www.ory.com/oauth2-openid-connect-do-you-need-use-cases-examples)
to read more about it.
