---
id: quickstart
title: Quickstart
---

import Mermaid from '@theme/Mermaid'
import CodeFromRemote from '@theme/CodeFromRemote'

Ory Kratos has several moving parts and getting everything right from the
beginning can be challenging. This getting started guide will help you install
Ory Kratos and some additional dependencies so that you can see how it works.

Please be aware that this guide is not a replacement for studying the docs. You
must understand core concepts and APIs to use Ory Kratos productively. This is
merely a guide to get you set up with some examples.

<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/JhlIZ3aChXc"
  frameborder="0"
  allowfullscreen
/>

## Use Case: Application Login and Registration

This section gives you some context on what we want to achieve and what tools we
need for that. You will also learn about the network set up we picked for this
guide.

This quickstart guide operates on the assumption that we are writing a NodeJS
app called **SecureApp**. This app is using nothing fancy - some ExpressJS and a
bit of HTML Templating using Handlebars. We do want to use TypeScript, but only
because it's more readable - not because we're doing anything out of the
ordinary!

You could pick any technology here, of course. This works with Swift, ReactJS,
or Angular (client side) as well as with PHP, Ruby, Python, Java (server side) -
you name it! We picked NodeJS + TypeScript because we believe it is the easiest
to understand, and because JavaScript and NodeJS are universally understood and
easy to install.

We know that SecureApp will need to have some type of dashboard and that it
needs users. Therefore, we need:

- Login
- Logout
- Registration
- Profile management ("update first name", "update avatar", etc.)
- Credentials Management ("add a new recovery email", "change password", etc.)
- Account Recovery ("password reset")
- Email Verification
- Two Factor Authentication
- "Sign in with Google" and "Sign in with GitHub"

and of course:

- A dashboard that shows
  `Hello {{ traits.name.first }} {{ traits.name.last }}!` which is only visible
  when the user is signed in.

## Setup

As you might already know, Ory Kratos is API-only; it does not have a UI or HTML
templating engine. We will implement all the user-facing UIs like "dashboard",
"login", and "registration" in our NodeJS SecureApp!

To ensure that no one can access the dashboard without prior authentication
(login), we can use a small piece of code (here ExpressJS) to do that:

<CodeFromRemote
  lang="js"
  link="https://github.com/ory/kratos-selfservice-ui-node/blob/master/src/middleware/simple.ts"
  src="https://raw.githubusercontent.com/ory/kratos-selfservice-ui-node/master/src/middleware/simple.ts"
/>

:::info

Ory Kratos is not just an API: it uses cookies, HTTP redirects, anti-CSRF tokens
and more so you don't have to.

:::

The SecureApp and Ory Kratos need to share cookies in order for anti-CSRF tokens
and login sessions to work. Because the quickstart runs on different ports on
`127.0.0.1` there is nothing we need to do to get this all working. In
environments where you have multiple sub-domains or reverse proxies, the set up
will be a bit more sophisticated. You can find more information about the
different set up possibilities in the
[Getting Cookies to Work on Multi-Domains Guide](guides/multi-domain-cookies.mdx).

Ory Kratos does not ship with an administrative user interface. You must
implement that yourself or choose the Ory Cloud offering (to be announced). In
this quickstart we will use Ory Kratos' CLI to interact with Ory Kratos' APIs.

The quickstart also comes with [MailSlurper](https://mailslurper.com), a mock
SMTP server the demo uses to show how email verification works.

### Clone Ory Kratos and Run it in Docker

To get this example working, you will need Git,
[Docker](https://docs.docker.com/get-docker/), and
[Docker Compose](https://docs.docker.com/compose/install/) installed on your
system. No other dependencies are required. Before you start, make sure that
Docker has enough disk space.

:::tip

This tutorial uses Docker-Compose volumes which have reported to run out of disk
space. Check the remaining disk space using `docker system df`. If the volumes
are above the 85% threshold,
[prune old Docker objects](https://docs.docker.com/config/pruning/) before you
start!

:::

:::tip

If you encounter build errors (e.g. network timeout), make sure that the network
is running correctly and run `make docker` again. If the problem persists, feel
free to [open an issue](https://github.com/ory/kratos/issues/new/choose).

:::

Let's clone Ory Kratos and run `docker-compose`:

```shell script
git clone https://github.com/ory/kratos.git
cd kratos
git checkout v0.7.4-alpha.1

docker-compose -f quickstart.yml -f quickstart-standalone.yml up --build --force-recreate
# If you have SELinux, run:
docker-compose -f quickstart.yml -f quickstart-selinux.yml -f quickstart-standalone.yml up --build --force-recreate
```

This might take a minute or two. Once the output slows down and logs indicate a
healthy system you're ready to roll! A healthy system will show something along
the lines of (the order of messages might be reversed):

```
kratos_1 | time="2020-01-20T14:52:13Z" level=info msg="Starting the admin httpd on: 0.0.0.0:4434"
kratos_1 | time="2020-01-20T14:52:13Z" level=info msg="Starting the public httpd on: 0.0.0.0:4433"
```

:::note

There are two important factors to get a fully functional system:

- You need to make sure that ports `4455`, `4433`, `4434`, and `4436`&nbsp;
  [are free](https://serverfault.com/questions/309052/check-if-port-is-open-or-closed-on-a-linux-server).
- Make sure to always use `127.0.0.1` as the hostname; never use `localhost`!
  This is important because browsers treat these two as separate domains and
  will therefore have issues with setting and using cookies correctly.

:::

You might notice that no database is being used in this example. Ory Kratos
supports SQLite, PostgreSQL, MySQL, and CockroachDB as database backends. For
the quickstart, we're mounting a persistent volume to store the SQLite database
in.

Future guides will explain how to set up a production system.

### Network Architecture

This demo makes use of several services:

1. [Ory Kratos](https://github.com/ory/kratos)
   - Public ("Browser") API (port 4433)
   - Admin API (port 4434) - This is only made public so we can test via the
     CLI.
2. [SecureApp](http://github.com/ory/kratos-selfservice-ui-node)
   - Public (port 4455) - an example application written in NodeJS that
     implements the login, registration, logout, dashboard, and other UIs.
3. [MailSlurper](https://github.com/mailslurper)
   - Public (port 4436) - a development SMTP server which Ory Kratos will use to
     send emails.

To better understand the application architecture, let's take a look at the
network configuration. This assumes that you have at least some understanding of
how Docker networks work:

<Mermaid
  chart={`
graph TD
subgraph hn[Host Network]
    B[Browser]
    B-->|Can access URLs via 127.0.0.1:4455|OKPHN
    B-->|Can access URLs via 127.0.0.1:4433|PAPI
    B-->|Can access UI via 127.0.0.1:4436|SMTPUI
    OKPHN([SecureApp exposed at :4455])
    SMTPUI([MailSlurper UI exposed at :4436])
    PAPI([Ory Kratos Public API exposed at :4433])
end
subgraph dn["Internal Docker Network (intranet)"]
    OKPHN-.->SA
    SMTPUI-.->SMTP
    PAPI-.->OK
    SA-->|Talks to and validates login sessions using|OK
    OK-->|Sends mail via|SMTP
    OK[Ory Kratos]
    SA["SecureApp (Ory Kratos SelfService UI Node Example)"]
    SMTP["SMTP Server (MailSlurper)"]
end
`}
/>

## Perform Registration, Login, and Logout

Enough theory, it's time to get this thing going! Let's start by trying to open
the dashboard - **go to
[127.0.0.1:4455/dashboard](http://127.0.0.1:4455/dashboard)**.

You should notice that you're ending up at the login endpoint instead of the
dashboard:

![Login screen of your secured app](images/quickstart/secureapp-login.png)

Looking at the network stack, you can see two redirects happening:

![Network trace of your secured app](images/quickstart/secureapp-login-ntrace.png)

Here's a play-by-play of what happened:

1. SecureApp used the Ory Kratos JavaScript language client to guard the
   `/dashboard` route. The Ory Kratos client checked the cookies from the
   request and saw you were not logged in.
2. The route guard redirected you from `/dashboard` to `/auth/login`. Ory
   Kratos' browser API requires a `<flow_id>` in order to log you in. It looked
   for this ID in the URL as a query parameter but couldn't find it.
3. `/auth/login` redirected you to
   `http://127.0.0.1:4434/self-service/login/browser`, which is one of Ory Kratos'
   APIs used for logging in browser-based applications.
4. Kratos handled `/self-service/login/browser` which created and persisted a
   new flow with `csrf_token`. Kratos would check no session exists, redirected
   the browser to `/auth/login?flow=<flow_id>`. If session exists already, the
   browser will be redirected to `urls.default_redirect_url` unless the query
   parameter `?refresh=true` was set.
5. SecureApp handled the `/auth/login` route, found the `<flow_id>` in the URL
   query parameter, and used it to make an HTTP request to
   `http://kratos:4434/self-service/login/flows?id=<flow_id>`. Notice the URI is
   `kratos:4434` because SecureApp is making a server-side HTTP request via
   Docker's private network to Ory Kratos' Admin API.
6. Ory Kratos responded with data which SecureApp used to render the HTML login
   form.

:::note

Your rendered login form should be a standard HTML `<form>`. AJAX requests will
not work!

:::

We can use `curl` to get an example of the payload that Ory Kratos responds
with. `GET` a valid `<flow_id>` and make a request to `/self-service/login/`:

```shell-session
$ flowId=$(curl -s -X GET \
    -H "Accept: application/json" \
    http://127.0.0.1:4433/self-service/login/api | jq -r '.id')
$ curl -s -X GET \
    -H "Accept: application/json" \
    "http://127.0.0.1:4433/self-service/login/flows?id=$flowId" | jq
{
  "id": "5b299430-0e68-441f-b1c5-a83657bdb25f",
  "type": "api",
  "expires_at": "2021-09-22T12:44:41.2436371Z",
  "issued_at": "2021-09-22T12:34:41.2436371Z",
  "request_url": "http://127.0.0.1:4433/self-service/login/api",
  "ui": {
    "action": "http://127.0.0.1:4433/self-service/login?flow=5b299430-0e68-441f-b1c5-a83657bdb25f",
    "method": "POST",
    "nodes": [
      {
        "type": "input",
        "group": "default",
        "attributes": {
          "name": "csrf_token",
          "type": "hidden",
          "value": "",
          "required": true,
          "disabled": false
        },
        "messages": [],
        "meta": {}
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "password_identifier",
          "type": "text",
          "value": "",
          "required": true,
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070004,
            "text": "ID",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "password",
          "type": "password",
          "required": true,
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070001,
            "text": "Password",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "method",
          "type": "submit",
          "value": "password",
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1010001,
            "text": "Sign in",
            "type": "info",
            "context": {}
          }
        }
      }
    ]
  },
  "created_at": "2021-09-22T12:34:41.244707Z",
  "updated_at": "2021-09-22T12:34:41.244707Z",
  "forced": false
}
```

This flow also works with Single Page Apps (SPA) and frameworks like Angular or
ReactJS. For more details about the specific flows (login, registration, logout,
etc.), head over to the [concept](concepts/index.md) chapter.

Let's move on to the next flow - registration! Click on "Register new account",
which initiates a flow similar to the one we just used:

![Registration screen of your secured app](images/quickstart/secureapp-registration.png)

The network trace should look familiar by now:

![Registration with network trace screen of your secured app](images/quickstart/secureapp-registration-ntrace.png)

To get an example of the payload for a registration flow, we can use the same
`curl` request as above, now to the `self-service/registration` endpoint.

```shell script
$ flowId=$(curl -s -X GET \
    -H "Accept: application/json" \
    http://127.0.0.1:4433/self-service/registration/api | jq -r '.id')

$ curl -s -X GET \
    -H "Accept: application/json" \
    "http://127.0.0.1:4433/self-service/registration/flows?id=$flowId" | jq

{
  "id": "4d1d11c8-12f0-4d29-8801-795c5072ed7a",
  "type": "api",
  "expires_at": "2021-09-22T18:10:11.0677811Z",
  "issued_at": "2021-09-22T18:00:11.0677811Z",
  "request_url": "http://127.0.0.1:4433/self-service/registration/api",
  "ui": {
    "action": "http://127.0.0.1:4433/self-service/registration?flow=4d1d11c8-12f0-4d29-8801-795c5072ed7a",
    "method": "POST",
    "nodes": [
      {
        "type": "input",
        "group": "default",
        "attributes": {
          "name": "csrf_token",
          "type": "hidden",
          "value": "",
          "required": true,
          "disabled": false
        },
        "messages": [],
        "meta": {}
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "traits.email",
          "type": "email",
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070002,
            "text": "E-Mail",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "password",
          "type": "password",
          "required": true,
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070001,
            "text": "Password",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "traits.name.first",
          "type": "text",
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070002,
            "text": "First Name",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "traits.name.last",
          "type": "text",
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1070002,
            "text": "Last Name",
            "type": "info"
          }
        }
      },
      {
        "type": "input",
        "group": "password",
        "attributes": {
          "name": "method",
          "type": "submit",
          "value": "password",
          "disabled": false
        },
        "messages": [],
        "meta": {
          "label": {
            "id": 1040001,
            "text": "Sign up",
            "type": "info",
            "context": {}
          }
        }
      }
    ]
  }
}
```

If we try to sign up using a password like `123456`, Krato's password policy
will complain:

![Registration with network trace screen of your secured app](images/quickstart/secureapp-registration-pwpolicy.png)

Setting a password that doesn't violate these policies, we will be immediately
redirected to the dashboard:

![SecureApp Dashboard](images/quickstart/secureapp-dashboard.png)

By clicking the "logout" icon in the top right, you will be redirected to the
login screen again where you will be able to use your credentials to log back in
again. Exciting!

### Understanding How Login and Registration Works

Head over to the [Self-Service Flows Chapter](self-service.mdx) for an in-depth
explanation of how each individual flow works.

### Email Verification

As you've signed up, an email was sent to the email address you used. Because
the quickstart uses a fake SMTP server, the email did not arrive in your inbox.
You can retrieve the email however by opening the MailSlurper UI at
[127.0.0.1:4436](http://127.0.0.1:4436).

You should see something like this:

![User Email Verification](images/quickstart/mailslurper-quickstart.png)

If not, hard refresh the tab or click on the home icon in the menu bar.

Next, click the verification link. You will end up at the dashboard, with a
verified email address (check the `verified` and `verified_at` field in the JSON
response):

![SecureApp Dashboard](images/quickstart/secureapp-verified-dashboard.png)

To re-request the verification email, fill out the form at
[127.0.0.1:4455/verify](http://127.0.0.1:4455/verify).

To learn more about verification recovery, head over to the
[Email and Phone Verification and Account Activation Documentation](self-service/flows/verify-email-account-activation.mdx).

### Account Recovery

The quickstart has account recovery enabled. To recover an account, log out and
click on "Reset password":

![Click on recover account](images/quickstart/quickstart-recover-i.png)

The next screen shows a HTML form where you enter your email address:

![Enter your email address](images/quickstart/quickstart-recover-ii.png)

Hit "submit" and check the emails for the account recovery message:

![Check your email for the password reset email](images/quickstart/quickstart-recover-iii.png)

Click the link, and change your password:

![Change your password](images/quickstart/quickstart-recover-iv.png)

You are now able to sign in with the new password. To learn more about account
recovery, head over to the
[Account Recovery Documentation](self-service/flows/account-recovery.mdx).

#### Configuration

You can find all configuration files used for this quickstart guide in
[`./contrib/quickstart/kratos`](https://github.com/ory/kratos/tree/v0.7.4-alpha.1/contrib/quickstart/kratos/email-password)
,
[`./quickstart.yml`](https://github.com/ory/kratos/blob/v0.7.4-alpha.1/quickstart.yml),
and
[`./quickstart-standalone.yml`](https://github.com/ory/kratos/blob/v0.7.4-alpha.1/quickstart-standalone.yml).
To understand what each of those configuration files does, consult the other
chapters of this documentation.

:::note

To get a minimal version of Ory Kratos running, you need to set configuration
values for
[`identity.default_schema_url`](https://github.com/ory/kratos/blob/v0.7.4-alpha.1/contrib/quickstart/kratos/email-password/kratos.yml#L75)
and
[`DSN`](https://github.com/ory/kratos/blob/v0.7.4-alpha.1/quickstart.yml#L42).
You should also configure
[`selfservice.flows.*.ui_url`](https://github.com/ory/kratos/blob/v0.7.4-alpha.1/contrib/quickstart/kratos/email-password/kratos.yml#L24)
or else Kratos will use fallback URLs.

:::

This is just scratching the surface of Ory Kratos, see [Next Steps](#next-steps)
for pointers how to continue.

## Cleaning Up Docker

To clean everything up, you need to bring down the Docker Compose environment
and remove all mounted volumes.

```shell script
docker-compose -f quickstart.yml down -v
docker-compose -f quickstart.yml rm -fsv
```

## Next Steps

Here is some information should you want to modify the quickstart:

**Social Login**

[Step-by-step guides](https://www.ory.sh/kratos/docs/next/guides/sign-in-with-github-google-facebook-linkedin)
to add sign up and login with popular OIDC providers to the Ory Kratos Quickstart, for example:

- [GitHub](https://www.ory.sh/kratos/docs/next/guides/sign-in-with-github-google-facebook-linkedin#github)
- [Google](https://www.ory.sh/kratos/docs/next/guides/sign-in-with-github-google-facebook-linkedin#google)
- [Facebook](https://www.ory.sh/kratos/docs/next/guides/sign-in-with-github-google-facebook-linkedin#facebook)  
  and
  [many more](https://www.ory.sh/kratos/docs/next/guides/sign-in-with-github-google-facebook-linkedin).

**Use a different database**

If you want to run the quickstart with PostgreSQL, run the following
docker-compose:
`docker-compose -f quickstart.yml -f quickstart-standalone.yml -f quickstart-postgres.yml up --build --force-recreate`

If you want to run the quickstart with CockroachDB, run the following
docker-compose:
`docker-compose -f quickstart.yml -f quickstart-standalone.yml -f quickstart-crdb.yml up --build --force-recreate`

If you want to run the quickstart with MySQL, run the following docker-compose:
`docker-compose -f quickstart.yml -f quickstart-standalone.yml -f quickstart-mysql.yml up --build --force-recreate`

**Change ports**

If you want to change ports for the SelfService-UI, you need to change them in
`quickstart.yml` as well as in the
`contrib/quickstart/kratos/email-password/kratos.yml` accordingly.

Note that you also need to change the ports for flows (error, settings,
recovery, verification, logout, login, registration).

The same procedure applies if you want to change ports for the Public UI/Admin
UI or the Mailslurper.

**Hooks**

To change the redirects happening after registration,login or a settings change,
take a look at this document: [Hooks](self-service/hooks.mdx).

If you delete the `session` hook from `kratos.yml`, the user will _not_ be
immediately signed in after registration.

### Quickstart Configuration

In this tutorial we use a simplified configuration.  
You can find it in
[`contrib/quickstart/kratos/email-password/kratos.yml`](https://github.com/ory/kratos/blob/master/contrib/quickstart/kratos/email-password/kratos.yml).
In the same place the
[identity schema](https://github.com/ory/kratos/blob/master/contrib/quickstart/kratos/email-password/identity.schema.json)
used in the tutorial can be found. The configuration gets loaded in
docker-compose as specified in the
[`quickstart.yml`](https://github.com/ory/kratos/blob/master/quickstart.yml).

<CodeFromRemote
  lang="yml"
  link="https://github.com/ory/kratos/blob/master/quickstart.yml"
  src="https://github.com/ory/kratos/blob/master/contrib/quickstart/kratos/email-password/kratos.yml"
/>

Have a look at the [reference configuration](./reference/configuration.md) for
further information on possible configuration options.
