Settings Flow

The Settings flow lets your end-users update their credentials and profile traits directly within your application. The entire interaction stays in your UI — no redirects to Verne-hosted pages.

CapabilitySupported
Change passwordYes
Update profile traits (email, display name, etc.)Yes
Link / unlink social providers (OIDC)Yes

How It Works

Settings flow requires an active user session. The session token is obtained after a successful login or password recovery.

Your backend                  Edge Gateway              Kratos
     │                              │                      │
     │── GET /v1/gate/auth/settings ─▶                     │
     │   Authorization: Bearer <api_key>                   │
     │   X-Session-Token: <kratos_session_token>           │
     │                              │── GET /self-service/settings/api ─▶
     │                              │   X-Session-Token: ...
     │                              │◀─ 200 flow JSON ──────
     │◀── 200 flow JSON ────────────│                      │
     │                              │                      │
     │── POST /v1/gate/auth/settings/submit?flow=<id> ─▶   │
     │   X-Session-Token: <kratos_session_token>           │
     │   { "method": "password", "password": "..." }       │
     │                              │── POST /self-service/settings?flow=<id> ─▶
     │                              │◀─ 200 updated identity ──
     │◀── 200 updated identity ─────│                      │

Security: Your vrn_gate_* API key never reaches the browser. The Kratos session token is user-scoped — it cannot access other users' data. The Edge Gateway verifies that the settings flow belongs to your tenant before proxying to Kratos.


API Reference

Initiate Settings Flow

GET /v1/gate/auth/settings

Creates a Kratos settings flow for the authenticated user and returns the flow descriptor with all available UI nodes.

Required headers

HeaderValue
AuthorizationBearer vrn_gate_live_sk_... — your tenant API key
X-Session-TokenThe user's Kratos session token from a prior login or recovery

Response (200 OK)

Returns the Kratos flow object. The ui.nodes array contains input nodes grouped by method:

GroupContents
defaultCSRF token
passwordNew password input + submit button
profileEditable trait inputs (email, name, etc.) + submit button
oidcSocial provider link/unlink buttons (only for enabled providers)
{
  "id": "flow_abc123",
  "type": "api",
  "expires_at": "2026-04-11T14:00:00Z",
  "ui": {
    "action": "https://...",
    "method": "POST",
    "nodes": [
      { "group": "default",  "attributes": { "name": "csrf_token", ... } },
      { "group": "password", "attributes": { "name": "password",   ... } },
      { "group": "profile",  "attributes": { "name": "traits.email", ... } }
    ]
  }
}

Example

curl https://api.vernesoft.com/v1/gate/auth/settings \
  -H 'Authorization: Bearer vrn_gate_live_sk_9f8a7...' \
  -H 'X-Session-Token: st_xyzabc...'

Submit Settings Flow

POST /v1/gate/auth/settings/submit?flow={flow_id}

Submits a settings change to Kratos. The flow_id must match the ID returned by the initiation step above.

Required headers

HeaderValue
AuthorizationBearer vrn_gate_live_sk_... — your tenant API key
X-Session-TokenThe same session token used to initiate the flow
Content-Typeapplication/json

Request body

Change password:

{
  "method": "password",
  "password": "new-secure-password-123!"
}

Update profile traits:

{
  "method": "profile",
  "traits": {
    "email": "new-email@example.com"
  }
}

Response (200 OK)

On success Kratos returns the updated identity and session state.

{
  "identity": {
    "id": "identity_123",
    "traits": { "email": "new-email@example.com", "tenant_id": "ten_001" }
  },
  "session": { ... }
}

If the submitted value fails validation (e.g. password too weak), Kratos returns 400 or 422 with the flow object containing error messages in the relevant UI nodes.

Example

curl -X POST 'https://api.vernesoft.com/v1/gate/auth/settings/submit?flow=flow_abc123' \
  -H 'Authorization: Bearer vrn_gate_live_sk_9f8a7...' \
  -H 'X-Session-Token: st_xyzabc...' \
  -H 'Content-Type: application/json' \
  -d '{ "method": "password", "password": "NewPassword456!" }'

Integration Guide

A typical "change password" screen in your application:

// ─── 1. Backend ───────────────────────────────────────────────────────────────
// Both calls must be made server-side — the API key must not leave your backend.

const GATE_API = 'https://api.vernesoft.com';
const GATE_KEY = 'vrn_gate_live_sk_...'; // Dashboard → API Keys → Gate

// Store the session token in your session after the user logs in.
// It is returned by /v1/gate/auth/login/submit as `session.token`.

async function initSettingsFlow(sessionToken) {
  const res = await fetch(`${GATE_API}/v1/gate/auth/settings`, {
    headers: {
      Authorization: `Bearer ${GATE_KEY}`,
      'X-Session-Token': sessionToken,
    },
  });
  if (!res.ok) throw new Error(`Gate error: ${res.status}`);
  return res.json();
}

async function submitSettingsFlow(flowId, sessionToken, body) {
  const res = await fetch(
    `${GATE_API}/v1/gate/auth/settings/submit?flow=${flowId}`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${GATE_KEY}`,
        'X-Session-Token': sessionToken,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    }
  );
  return { status: res.status, data: await res.json() };
}

// ─── Example: Express route handlers ──────────────────────────────────────────

// GET /account/settings/flow
app.get('/account/settings/flow', requireAuth, async (req, res) => {
  const flow = await initSettingsFlow(req.session.kratosToken);
  res.json(flow);
});

// POST /account/settings/password
app.post('/account/settings/password', requireAuth, async (req, res) => {
  const flow = await initSettingsFlow(req.session.kratosToken);
  const result = await submitSettingsFlow(flow.id, req.session.kratosToken, {
    method: 'password',
    password: req.body.new_password,
  });
  res.status(result.status).json(result.data);
});
// ─── 2. Frontend ──────────────────────────────────────────────────────────────
// Your change-password form — fetch the flow from your server, submit to server.

async function changePassword(newPassword) {
  // 1. Get flow from your backend
  const flow = await fetch('/account/settings/flow').then(r => r.json());

  // 2. Submit the password change through your backend
  const result = await fetch('/account/settings/password', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ new_password: newPassword }),
  }).then(r => r.json());

  if (result.ui?.messages?.some(m => m.type === 'error')) {
    // Show validation errors from Kratos
    const msg = result.ui.messages.map(m => m.text).join(', ');
    throw new Error(msg);
  }

  return result; // success
}

Where Does the Session Token Come From?

The Kratos session token is returned when a user successfully authenticates via:

  • Login flow — returned in the session.token field of the POST /v1/gate/auth/login/submit response.
  • Recovery flow — returned after phase 2 (code verification) in the same session.token field. This is the primary use case: a user who has just recovered their account is immediately redirected to change their password.

Store the session token server-side (e.g. in your session store). Do not expose it to the browser.


Error Handling

HTTP statusMeaning
400Missing or malformed X-Session-Token header
401Session token is expired or invalid — user must log in again
403Flow does not belong to this tenant, or has expired (TTL: 1 hour)
422Validation error (e.g. password too short) — check ui.messages in the response body
502Kratos unreachable — transient error, retry with back-off