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.
| Capability | Supported |
|---|---|
| Change password | Yes |
| 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
| Header | Value |
|---|---|
Authorization | Bearer vrn_gate_live_sk_... — your tenant API key |
X-Session-Token | The 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:
| Group | Contents |
|---|---|
default | CSRF token |
password | New password input + submit button |
profile | Editable trait inputs (email, name, etc.) + submit button |
oidc | Social 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
| Header | Value |
|---|---|
Authorization | Bearer vrn_gate_live_sk_... — your tenant API key |
X-Session-Token | The same session token used to initiate the flow |
Content-Type | application/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.tokenfield of thePOST /v1/gate/auth/login/submitresponse. - Recovery flow — returned after phase 2 (code verification) in the same
session.tokenfield. 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 status | Meaning |
|---|---|
400 | Missing or malformed X-Session-Token header |
401 | Session token is expired or invalid — user must log in again |
403 | Flow does not belong to this tenant, or has expired (TTL: 1 hour) |
422 | Validation error (e.g. password too short) — check ui.messages in the response body |
502 | Kratos unreachable — transient error, retry with back-off |