Skip to main content
The login flow allows users to authenticate and create sessions in Ory Kratos. It supports multiple authentication methods and multi-factor authentication (MFA).

Flow initialization

Login flows can be initiated for browser-based applications or API clients.

Browser flows

Initialize a login flow for browser applications:
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/browser' \
  -H 'Accept: application/json'
Query parameters:
  • refresh - Set to true to refresh an existing session and reset the authenticated_at timestamp
  • aal - Request a specific Authenticator Assurance Level (aal1 or aal2 for MFA)
  • return_to - URL to redirect to after successful login
  • login_challenge - Hydra OAuth2 login challenge (when acting as an OAuth2 provider)
  • organization - Organization ID for multi-tenant deployments
  • identity_schema - Specific identity schema to use for this flow
Response:
{
  "id": "f8a5c8e9-5b1c-4f8e-9b3a-1e5f7c9d8e6b",
  "type": "browser",
  "expires_at": "2026-03-03T12:00:00Z",
  "issued_at": "2026-03-03T11:00:00Z",
  "request_url": "https://{project}.projects.oryapis.com/self-service/login/browser",
  "ui": {
    "action": "https://{project}.projects.oryapis.com/self-service/login?flow=f8a5c8e9-5b1c-4f8e-9b3a-1e5f7c9d8e6b",
    "method": "POST",
    "nodes": [...]
  },
  "requested_aal": "aal1",
  "state": "choose_method"
}

API flows

For native applications (mobile, desktop, server-to-server):
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/api' \
  -H 'Accept: application/json'
Additional query parameters:
  • return_session_token_exchange_code - Request a code to exchange for a session token after login

Authentication methods

The login flow supports multiple authentication strategies that can be combined for MFA.

Password

Authenticate with identifier (email, username) and password:
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "password",
    "identifier": "user@example.com",
    "password": "your-secure-password"
  }'

OIDC (Social sign-in)

Sign in with OAuth2/OIDC providers (Google, GitHub, Microsoft, etc.):
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "oidc",
    "provider": "google"
  }'
Supported providers include:
  • Google
  • GitHub
  • Microsoft
  • Apple
  • GitLab
  • Generic OIDC providers

Passkeys (WebAuthn)

Authenticate using FIDO2/WebAuthn credentials:
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "passkey",
    "passkey_login": "<webauthn_response>"
  }'

TOTP (Time-based one-time passwords)

Second factor authentication with authenticator apps:
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "totp",
    "totp_code": "123456"
  }'

Lookup secrets (Backup codes)

Authenticate using single-use recovery codes:
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "lookup_secret",
    "lookup_secret": "backup-code-1234"
  }'

WebAuthn

Hardware security keys and platform authenticators:
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "method": "webauthn",
    "webauthn_login": "<webauthn_assertion>"
  }'

Multi-factor authentication

Request AAL2 (second factor) authentication:
1

Check current AAL

Verify the user’s current session authenticator assurance level:
curl -X GET \
  'https://{project}.projects.oryapis.com/sessions/whoami' \
  -H 'Cookie: ory_kratos_session=...'
Response includes authenticator_assurance_level: "aal1" or "aal2".
2

Initialize AAL2 flow

Request an AAL2 login flow:
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/browser?aal=aal2' \
  -H 'Cookie: ory_kratos_session=...'
3

Complete second factor

Submit the second factor (TOTP, WebAuthn, or lookup secret):
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/json' \
  -H 'Cookie: ory_kratos_session=...' \
  -d '{
    "method": "totp",
    "totp_code": "123456"
  }'
AAL2 flows require an active AAL1 session. If no session exists, the request will fail with session_aal1_required.

Session refresh

Refresh an existing session to update the authenticated_at timestamp:
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/browser?refresh=true' \
  -H 'Cookie: ory_kratos_session=...'
This forces the user to re-authenticate even if they have an active session.

Fetching an existing flow

Retrieve a login flow by its ID:
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/flows?id=<flow_id>' \
  -H 'Cookie: ory_kratos_session=...'

Error handling

Common error cases:
Status: 400 Bad RequestThe user is already authenticated. Either use ?refresh=true to re-authenticate, ?aal=aal2 to upgrade to MFA, or redirect to the application.

Complete flow example

# 1. Initialize the flow
curl -X GET 'https://{project}.projects.oryapis.com/self-service/login/browser'

# Browser is redirected to UI with flow ID
# User visits: https://your-app.com/login?flow=<flow_id>

# 2. Fetch the flow
curl -X GET \
  'https://{project}.projects.oryapis.com/self-service/login/flows?id=<flow_id>'

# 3. Submit credentials
curl -X POST \
  'https://{project}.projects.oryapis.com/self-service/login?flow=<flow_id>' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'method=password&identifier=user@example.com&password=secret&csrf_token=...'

# Browser is redirected with session cookie set

API reference

EndpointMethodDescription
/self-service/login/browserGETInitialize browser login flow
/self-service/login/apiGETInitialize API login flow
/self-service/login/flowsGETGet login flow by ID
/self-service/loginPOSTSubmit login flow
All endpoints are defined in selfservice/flow/login/handler.go:44-49

Build docs developers (and LLMs) love