@ory/argus 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/assets/skills/auth-setup/SKILL.md +209 -49
- package/assets/skills/login-flow/SKILL.md +192 -17
- package/assets/skills/social-login/SKILL.md +9 -0
- package/dist/adapters.d.ts +104 -0
- package/dist/adapters.js +201 -0
- package/dist/agent-auth.js +13 -0
- package/dist/contract-suite.d.ts +87 -0
- package/dist/contract-suite.js +239 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/dist/lifecycle.js +129 -0
- package/dist/local/configs.d.ts +2 -2
- package/dist/local/configs.js +10 -1
- package/dist/local/manager.d.ts +6 -4
- package/dist/local/manager.js +91 -16
- package/dist/permissions.d.ts +15 -3
- package/dist/permissions.js +17 -2
- package/dist/testing.d.ts +214 -0
- package/dist/testing.js +372 -0
- package/dist/tool-catalog.d.ts +6 -0
- package/dist/tool-catalog.js +54 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ const session = await client.verifySession(sessionToken);
|
|
|
16
16
|
const result = await client.checkPermission({
|
|
17
17
|
namespace: "AgentTools",
|
|
18
18
|
object: "Bash",
|
|
19
|
-
relation: "
|
|
19
|
+
relation: "use",
|
|
20
20
|
subjectId: `session:${sessionId}`,
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -57,7 +57,7 @@ async function onBeforeTool(toolName: string, sessionId: string) {
|
|
|
57
57
|
const result = await client.checkPermission({
|
|
58
58
|
namespace: "AgentTools",
|
|
59
59
|
object: toolName,
|
|
60
|
-
relation: "
|
|
60
|
+
relation: "use",
|
|
61
61
|
subjectId: `session:${sessionId}`,
|
|
62
62
|
});
|
|
63
63
|
if (result.allowed === false) {
|
|
@@ -84,7 +84,7 @@ The wrapped Ory client. One instance per harness session.
|
|
|
84
84
|
| Sessions and tokens | `verifySession`, `introspectToken`, `classifyError` |
|
|
85
85
|
| Permission checks | `checkPermission`, `batchCheckPermissions`, `checkMcpPermission` |
|
|
86
86
|
| Principals (who is acting) | `setUserPrincipal`, `setAgentPrincipal` |
|
|
87
|
-
| Delegation
|
|
87
|
+
| Delegation relations | `createRelationship`, `deleteRelationship` |
|
|
88
88
|
| Tracing | `tracer` (see Tracer below) |
|
|
89
89
|
|
|
90
90
|
### Identity gates
|
|
@@ -32,6 +32,48 @@ If you find yourself writing `flow.ui.nodes.map(...)` to render auth UI,
|
|
|
32
32
|
stop and switch to Ory Elements. Custom node rendering is a fallback,
|
|
33
33
|
not a parallel option.
|
|
34
34
|
|
|
35
|
+
## Correctness contract — satisfy these or the app will 404 / 500 / CSRF
|
|
36
|
+
|
|
37
|
+
Almost every "Ory doesn't work" report comes from one of four wiring
|
|
38
|
+
mistakes in the generated app — **not** from Ory itself and **not** from
|
|
39
|
+
the agent plugin governing this session. Treat each as a hard requirement,
|
|
40
|
+
and verify all four (Step 10) before telling the user the app is ready.
|
|
41
|
+
|
|
42
|
+
1. **Ory must be first-party (same site as your app), or every flow fails
|
|
43
|
+
CSRF.** Ory's browser flows depend on a CSRF cookie. If the browser calls
|
|
44
|
+
Ory on a *different* site than your app, the browser drops that cookie and
|
|
45
|
+
you get `CSRF token mismatch`, 403s, or redirect loops. So the browser SDK
|
|
46
|
+
URL must be **your own origin** — never `https://<slug>.projects.oryapis.com`
|
|
47
|
+
directly:
|
|
48
|
+
- **Local dev against Ory Network** — run `ory tunnel` and point the SDK at
|
|
49
|
+
the tunnel (`http://localhost:4000`). See Step 9.
|
|
50
|
+
- **Local dev against the local stack** — the gateway is already on
|
|
51
|
+
`http://localhost:4000` (first-party to `localhost`). Use `http://localhost`,
|
|
52
|
+
not `127.0.0.1`.
|
|
53
|
+
- **Production** — serve Ory from your own domain via an Ory Network custom
|
|
54
|
+
domain (CNAME) or the `@ory/nextjs` proxy, and set the SDK URL to that.
|
|
55
|
+
2. **The app's routes must match what Ory redirects to, or you get 404s.** Ory
|
|
56
|
+
redirects the browser to the self-service UI URLs configured on the project
|
|
57
|
+
(login, registration, recovery, verification, settings, **error**). If a page
|
|
58
|
+
lives at `/auth/login` but the project points at `/login`, the redirect 404s.
|
|
59
|
+
Pick one set of paths and make these four agree: the page files you create,
|
|
60
|
+
the `@ory/nextjs` UI-path config, the project's `selfservice.flows.*.ui_url`
|
|
61
|
+
(+ `error.ui_url`), and the middleware path lists. Always create an error
|
|
62
|
+
page — Ory redirects there on any flow error. See Step 8.
|
|
63
|
+
3. **Server-side session/flow calls must forward the request cookies, or they
|
|
64
|
+
500/401.** A `FrontendApi` built with `credentials: "include"` only sends
|
|
65
|
+
cookies in the **browser**. In Next.js server components, route handlers, and
|
|
66
|
+
middleware you must forward the incoming request's cookies — the `@ory/nextjs`
|
|
67
|
+
server helpers do this for you. Reusing the browser client on the server is
|
|
68
|
+
the single most common 500. See Step 7.
|
|
69
|
+
4. **The SDK URL env var must be set at runtime, or the SDK 500s on
|
|
70
|
+
construction.** Validate `NEXT_PUBLIC_ORY_SDK_URL` (browser) / `ORY_SDK_URL`
|
|
71
|
+
(server) at startup and fail with a clear message instead of constructing the
|
|
72
|
+
client with `undefined`. See Step 4.
|
|
73
|
+
|
|
74
|
+
If a flow still fails after all four hold, isolate it with the **App bug vs.
|
|
75
|
+
plugin bug** triage in Step 10 before assuming a bug in Ory or in the plugin.
|
|
76
|
+
|
|
35
77
|
## Step 1: Check prerequisites
|
|
36
78
|
|
|
37
79
|
Before installing the Ory CLI, decide where the auth backend will run:
|
|
@@ -107,29 +149,54 @@ React island or a small client bundle so you still get the Elements UI
|
|
|
107
149
|
on the auth routes. Rendering flow nodes directly on the server is the
|
|
108
150
|
fallback of last resort.
|
|
109
151
|
|
|
152
|
+
**Match the installed major versions.** `@ory/nextjs` and
|
|
153
|
+
`@ory/elements-react` change import names and component props across major
|
|
154
|
+
versions. After installing, run `npm ls @ory/nextjs @ory/elements-react`
|
|
155
|
+
and follow the docs and TypeScript types for *that* major — do not assume
|
|
156
|
+
symbol names from memory. A missing import or a prop type error at build
|
|
157
|
+
time is a version mismatch in the generated app, not a plugin bug.
|
|
158
|
+
|
|
110
159
|
## Step 4: Configure the Ory SDK
|
|
111
160
|
|
|
112
|
-
|
|
113
|
-
|
|
161
|
+
For **Next.js**, prefer wiring Ory through `@ory/nextjs` (its config proxies
|
|
162
|
+
Ory under your own origin and supplies cookie-forwarding server helpers — this
|
|
163
|
+
satisfies contract #1 and #3 for free). Build a hand-rolled `FrontendApi`
|
|
164
|
+
client only for **browser** code in a React SPA, or for explicit client-side
|
|
165
|
+
calls.
|
|
166
|
+
|
|
167
|
+
When you do build a client, read the URL from an env var and **validate it** —
|
|
168
|
+
constructing the SDK with an `undefined` `basePath` is a 500 waiting to happen
|
|
169
|
+
(contract #4):
|
|
114
170
|
|
|
115
171
|
```typescript
|
|
116
172
|
import { Configuration, FrontendApi } from "@ory/client-fetch";
|
|
117
173
|
|
|
174
|
+
const sdkUrl = process.env.NEXT_PUBLIC_ORY_SDK_URL ?? process.env.ORY_SDK_URL;
|
|
175
|
+
if (!sdkUrl) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
"Ory SDK URL is not set. Set NEXT_PUBLIC_ORY_SDK_URL (browser) " +
|
|
178
|
+
"or ORY_SDK_URL (server) — see Step 4.",
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Browser-only: `credentials: "include"` sends cookies from browser code.
|
|
183
|
+
// Do NOT reuse this client in server components / route handlers / middleware
|
|
184
|
+
// (contract #3) — use the @ory/nextjs server helpers there.
|
|
118
185
|
const ory = new FrontendApi(
|
|
119
|
-
new Configuration({
|
|
120
|
-
basePath: process.env.NEXT_PUBLIC_ORY_SDK_URL || process.env.ORY_SDK_URL,
|
|
121
|
-
credentials: "include",
|
|
122
|
-
})
|
|
186
|
+
new Configuration({ basePath: sdkUrl, credentials: "include" }),
|
|
123
187
|
);
|
|
124
188
|
|
|
125
189
|
export default ory;
|
|
126
190
|
```
|
|
127
191
|
|
|
128
|
-
Add to the project's `.env` or `.env.local
|
|
192
|
+
Add to the project's `.env` or `.env.local`. The **browser** value
|
|
193
|
+
(`NEXT_PUBLIC_ORY_SDK_URL`) must be your own origin (contract #1), so in local
|
|
194
|
+
development point it at the tunnel or the local gateway, not at `*.oryapis.com`:
|
|
129
195
|
|
|
130
196
|
```bash
|
|
131
|
-
|
|
132
|
-
|
|
197
|
+
# Local dev (Ory Tunnel for Network, or the local stack gateway):
|
|
198
|
+
NEXT_PUBLIC_ORY_SDK_URL=http://localhost:4000
|
|
199
|
+
# Server-side only (safe to be the direct project URL):
|
|
133
200
|
ORY_SDK_URL=https://<project-slug>.projects.oryapis.com
|
|
134
201
|
```
|
|
135
202
|
|
|
@@ -166,7 +233,9 @@ Create the following page structure:
|
|
|
166
233
|
- `app/auth/registration/page.tsx` — `<Registration flow={flow} />`
|
|
167
234
|
- `app/auth/recovery/page.tsx` — `<Recovery flow={flow} />`
|
|
168
235
|
- `app/auth/verification/page.tsx` — `<Verification flow={flow} />`
|
|
169
|
-
- `app/auth/settings/page.tsx` — `<Settings flow={flow}
|
|
236
|
+
- `app/auth/settings/page.tsx` — `<Settings flow={flow} />`, wrapped in
|
|
237
|
+
`<SessionProvider>` from `@ory/elements-react/client` (Settings is the only
|
|
238
|
+
flow that needs it — see {{REF_LOGIN_FLOW}})
|
|
170
239
|
|
|
171
240
|
Each page initializes its flow with `getLoginFlow`, `getRegistrationFlow`,
|
|
172
241
|
etc. from `@ory/nextjs/app`, then hands the flow to the matching
|
|
@@ -191,17 +260,27 @@ with the user that an Ory Elements island is not viable.
|
|
|
191
260
|
|
|
192
261
|
## Step 7: Add session middleware
|
|
193
262
|
|
|
194
|
-
Protect authenticated routes by checking the session
|
|
263
|
+
Protect authenticated routes by checking the session. **Where this code runs
|
|
264
|
+
matters** (contract #3):
|
|
195
265
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
266
|
+
- **Browser code** can use the `credentials: "include"` client from Step 4
|
|
267
|
+
directly — the browser attaches the session cookie:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const session = await ory.toSession(); // browser only
|
|
271
|
+
if (!session) {
|
|
272
|
+
// Redirect to login
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
- **Server code** (server components, route handlers, middleware) must forward
|
|
277
|
+
the incoming request's cookies. Do **not** reuse the browser client — it has
|
|
278
|
+
no cookies on the server and will throw (→ 500) or always return 401. Use the
|
|
279
|
+
`@ory/nextjs` server helpers, which read and forward the request cookies for
|
|
280
|
+
you.
|
|
202
281
|
|
|
203
282
|
For Next.js, prefer the official `@ory/nextjs` middleware helper since
|
|
204
|
-
it pairs with the Elements pages:
|
|
283
|
+
it pairs with the Elements pages and handles server-side cookie forwarding:
|
|
205
284
|
|
|
206
285
|
```typescript
|
|
207
286
|
import { createOryMiddleware } from "@ory/nextjs/middleware";
|
|
@@ -216,37 +295,118 @@ export const config = {
|
|
|
216
295
|
};
|
|
217
296
|
```
|
|
218
297
|
|
|
219
|
-
## Step 8:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
298
|
+
## Step 8: Align the project's routes and return URLs with your app
|
|
299
|
+
|
|
300
|
+
This step prevents the 404s in contract #2. Ory redirects the browser to the
|
|
301
|
+
self-service UI URLs it has configured; those must point at pages your app
|
|
302
|
+
actually serves.
|
|
303
|
+
|
|
304
|
+
**Pick one route convention and use it everywhere.** This guide uses
|
|
305
|
+
`/auth/login`, `/auth/registration`, `/auth/recovery`, `/auth/verification`,
|
|
306
|
+
`/auth/settings`, and `/auth/error`. The page files, the middleware path lists,
|
|
307
|
+
and the project config below must all use the same paths.
|
|
308
|
+
|
|
309
|
+
1. **Create an error page.** Ory redirects to the error UI on *any* flow error;
|
|
310
|
+
if it doesn't exist you get a 404 (or a blank page) instead of a readable
|
|
311
|
+
message. Create `app/auth/error/page.tsx` rendering the Elements error
|
|
312
|
+
component (`<Error>` / the flow-error view for your installed version).
|
|
313
|
+
|
|
314
|
+
2. **Tell Ory where the pages are.**
|
|
315
|
+
- **Next.js with `@ory/nextjs`:** declare the UI paths in its config (the
|
|
316
|
+
UI-path overrides in `ory.config.ts`) so its helpers, middleware, and proxy
|
|
317
|
+
route to your pages. The SDK serves the UI under your own origin, so you
|
|
318
|
+
usually do not also edit the project's `ui_url`s.
|
|
319
|
+
- **React SPA / no `@ory/nextjs` proxy:** set the project's self-service UI
|
|
320
|
+
URLs to your app's routes:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
ory patch project <project-id> \
|
|
324
|
+
--replace '/services/identity/config/selfservice/flows/login/ui_url="https://your-domain.com/auth/login"' \
|
|
325
|
+
--replace '/services/identity/config/selfservice/flows/registration/ui_url="https://your-domain.com/auth/registration"' \
|
|
326
|
+
--replace '/services/identity/config/selfservice/flows/recovery/ui_url="https://your-domain.com/auth/recovery"' \
|
|
327
|
+
--replace '/services/identity/config/selfservice/flows/verification/ui_url="https://your-domain.com/auth/verification"' \
|
|
328
|
+
--replace '/services/identity/config/selfservice/flows/settings/ui_url="https://your-domain.com/auth/settings"' \
|
|
329
|
+
--replace '/services/identity/config/selfservice/flows/error/ui_url="https://your-domain.com/auth/error"'
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
3. **Allow redirects back to your app** (every origin you run on — dev and prod):
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
ory patch project <project-id> \
|
|
336
|
+
--replace '/services/identity/config/selfservice/allowed_return_urls=["http://localhost:3000", "https://your-domain.com"]'
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Step 9: Serve Ory first-party (required — contract #1)
|
|
340
|
+
|
|
341
|
+
The browser must reach Ory on the same site as your app, or cookie-based flows
|
|
342
|
+
fail CSRF. How you achieve that depends on the environment:
|
|
343
|
+
|
|
344
|
+
- **Local dev against Ory Network — run the Ory Tunnel.** This is required, not
|
|
345
|
+
optional:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
ory tunnel http://localhost:3000 --project <project-slug>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
It runs a proxy on `http://localhost:4000` that serves Ory first-party to
|
|
352
|
+
`localhost`. Point `NEXT_PUBLIC_ORY_SDK_URL` at the tunnel
|
|
353
|
+
(`http://localhost:4000`) — **not** at `https://<slug>.projects.oryapis.com`.
|
|
354
|
+
|
|
355
|
+
- **Local dev against the local stack** — the gateway is already first-party on
|
|
356
|
+
`http://localhost:4000`; no tunnel needed (switch to {{REF_LOCAL_DEV}}).
|
|
357
|
+
|
|
358
|
+
- **Production** — serve Ory from your own domain via an Ory Network custom
|
|
359
|
+
domain (CNAME) or the `@ory/nextjs` proxy, and set the browser SDK URL to that
|
|
360
|
+
same-origin path.
|
|
361
|
+
|
|
362
|
+
If you skip this step, login will appear to work but `whoami`/`toSession` will
|
|
363
|
+
return 401 and form submits will fail with a CSRF error — the classic symptom of
|
|
364
|
+
a cross-site SDK URL.
|
|
365
|
+
|
|
366
|
+
## Step 10: Verify the setup — and confirm it's correct, not just present
|
|
367
|
+
|
|
368
|
+
Do not declare success on "the pages render." Run these checks; each maps to a
|
|
369
|
+
contract item so a failure points straight at the cause.
|
|
370
|
+
|
|
371
|
+
1. **SDK URL is first-party (contract #1).** Confirm the browser SDK URL is your
|
|
372
|
+
own origin (the tunnel/gateway in dev), not `*.oryapis.com`. In the browser
|
|
373
|
+
devtools Network tab, the flow requests should go to your origin and the
|
|
374
|
+
response should `Set-Cookie` a CSRF cookie.
|
|
375
|
+
2. **The flow API itself works (isolates Ory from your app).** Against the URL
|
|
376
|
+
the browser uses:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
curl -i "$NEXT_PUBLIC_ORY_SDK_URL/self-service/login/browser"
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Expect `200` with a `Set-Cookie: csrf_token...` header. If this fails, the
|
|
383
|
+
problem is Ory config / the tunnel — not your app code.
|
|
384
|
+
3. **Every redirect target exists (contract #2).** Visit `/auth/login`,
|
|
385
|
+
`/auth/registration`, `/auth/recovery`, `/auth/verification`,
|
|
386
|
+
`/auth/settings`, and `/auth/error` directly. Each must return `200`, not
|
|
387
|
+
`404`. A 404 here means a route/`ui_url` mismatch (Step 8).
|
|
388
|
+
4. **End-to-end:** register a test account, log in, confirm a protected route
|
|
389
|
+
loads while signed in and redirects to login when signed out, then test
|
|
390
|
+
recovery and verification.
|
|
391
|
+
|
|
392
|
+
### App bug vs. plugin bug
|
|
393
|
+
|
|
394
|
+
These are two different systems. Keep them straight so issues are filed in the
|
|
395
|
+
right place:
|
|
396
|
+
|
|
397
|
+
- **The generated app** (login pages, CSRF, sessions, the `/auth/*` routes) is
|
|
398
|
+
ordinary code running in the user's project. A `404`/`500`/CSRF on an `/auth/*`
|
|
399
|
+
route, or in the browser Network tab, is an **app/config** issue — work the
|
|
400
|
+
four contract items and the checks above.
|
|
401
|
+
- **The agent plugin** governs *this coding session* — it authenticates the
|
|
402
|
+
agent, checks Ory Permissions before each tool call, and writes trace spans. It
|
|
403
|
+
never serves your app's HTTP routes. Diagnose it with
|
|
404
|
+
`{{NPX}} status` and the debug log (`ORY_AGENT_DEBUG=true`), **not** by looking
|
|
405
|
+
at your app's auth pages.
|
|
406
|
+
|
|
407
|
+
Quick triage: if `curl` to the flow API (check 2) succeeds but the app page
|
|
408
|
+
fails, it's the app. If `curl` fails, it's Ory/tunnel config. Neither is the
|
|
409
|
+
plugin unless `{{NPX}} status` reports a problem.
|
|
250
410
|
|
|
251
411
|
## Customization
|
|
252
412
|
|
|
@@ -24,6 +24,27 @@ rendering belongs in the fallback section at the end of this skill.
|
|
|
24
24
|
no per-app rewrite needed.
|
|
25
25
|
- Works in Next.js (App Router and Pages Router) and any React SPA.
|
|
26
26
|
|
|
27
|
+
## Avoid the 404 / 500 / CSRF traps
|
|
28
|
+
|
|
29
|
+
These pages only work if the surrounding wiring is correct — most "the login
|
|
30
|
+
page is broken" reports are wiring, not the pages or the plugin. If you have not
|
|
31
|
+
run {{REF_AUTH_SETUP}}, do that first; it establishes the four invariants these
|
|
32
|
+
pages depend on:
|
|
33
|
+
|
|
34
|
+
- **First-party SDK URL** — the browser must reach Ory on your own origin (the
|
|
35
|
+
Ory Tunnel or local gateway, `http://localhost:4000` in dev), never
|
|
36
|
+
`https://<slug>.projects.oryapis.com` directly, or every form submit fails
|
|
37
|
+
with a CSRF error.
|
|
38
|
+
- **Routes match the project config** — the `/auth/*` paths below must match the
|
|
39
|
+
project's self-service `ui_url`s and the middleware lists, and you must create
|
|
40
|
+
an `/auth/error` page, or Ory's redirects 404.
|
|
41
|
+
- **Server calls forward cookies** — server-side session/flow reads must forward
|
|
42
|
+
the request cookies (the `@ory/nextjs` server helpers do this); reusing the
|
|
43
|
+
browser client on the server 500s.
|
|
44
|
+
|
|
45
|
+
See the **Correctness contract** and the **App bug vs. plugin bug** triage in
|
|
46
|
+
{{REF_AUTH_SETUP}} for the full detail.
|
|
47
|
+
|
|
27
48
|
## Before you start
|
|
28
49
|
|
|
29
50
|
Check the project setup:
|
|
@@ -243,6 +264,7 @@ Create `app/auth/settings/page.tsx`:
|
|
|
243
264
|
|
|
244
265
|
```typescript
|
|
245
266
|
import { Settings } from "@ory/elements-react/theme"
|
|
267
|
+
import { SessionProvider } from "@ory/elements-react/client"
|
|
246
268
|
import { getSettingsFlow, OryPageLayout } from "@ory/nextjs/app"
|
|
247
269
|
|
|
248
270
|
export default async function SettingsPage(props: {
|
|
@@ -257,31 +279,62 @@ export default async function SettingsPage(props: {
|
|
|
257
279
|
|
|
258
280
|
return (
|
|
259
281
|
<OryPageLayout>
|
|
260
|
-
<
|
|
282
|
+
<SessionProvider>
|
|
283
|
+
<Settings flow={flow} />
|
|
284
|
+
</SessionProvider>
|
|
261
285
|
</OryPageLayout>
|
|
262
286
|
)
|
|
263
287
|
}
|
|
264
288
|
```
|
|
265
289
|
|
|
290
|
+
Unlike the other flows, **`<Settings>` must be wrapped in `<SessionProvider>`**
|
|
291
|
+
from `@ory/elements-react/client`. Settings renders session-dependent controls
|
|
292
|
+
(connected social accounts, unlinking, logout of other sessions) that read from
|
|
293
|
+
the session context; the pre-auth pages (login, registration, recovery,
|
|
294
|
+
verification) have no session yet and do not need the provider.
|
|
295
|
+
|
|
266
296
|
### Settings — React SPA
|
|
267
297
|
|
|
268
298
|
Initialize with `createBrowserSettingsFlow` and render
|
|
269
|
-
`<Settings flow={flow} />` from `@ory/elements-react/theme
|
|
299
|
+
`<Settings flow={flow} />` from `@ory/elements-react/theme`, wrapped in
|
|
300
|
+
`<SessionProvider>` from `@ory/elements-react/client` (the same requirement
|
|
301
|
+
as the App Router page above — Settings reads session-dependent state). The
|
|
270
302
|
component handles password changes, profile traits, MFA enrollment,
|
|
271
303
|
and connected social providers.
|
|
272
304
|
|
|
305
|
+
## Build the error page (do not skip — missing it causes 404s)
|
|
306
|
+
|
|
307
|
+
Ory redirects the browser to the error UI on **any** flow error (expired flow,
|
|
308
|
+
validation failure, misconfiguration). If that route doesn't exist the user
|
|
309
|
+
hits a 404 or a blank page instead of a readable message, and the failure looks
|
|
310
|
+
like a plugin bug when it's just a missing page.
|
|
311
|
+
|
|
312
|
+
Create `app/auth/error/page.tsx` and render the Elements error view for your
|
|
313
|
+
installed version (`getFlowError` from `@ory/nextjs/app` + the Elements error
|
|
314
|
+
component). Make sure its path matches the project's `error.ui_url`
|
|
315
|
+
(see {{REF_AUTH_SETUP}}, Step 8). For a React SPA, fetch the error with
|
|
316
|
+
`getFlowError({ id })` from the `error` query param and display
|
|
317
|
+
`error.error.message`.
|
|
318
|
+
|
|
273
319
|
## Add session management
|
|
274
320
|
|
|
275
|
-
Create a utility to check the current session
|
|
321
|
+
Create a **browser** utility to check the current session. (For server
|
|
322
|
+
components, route handlers, and middleware, use the `@ory/nextjs` server
|
|
323
|
+
helpers instead — they forward the request cookies. Reusing this browser client
|
|
324
|
+
on the server has no cookies and will 500/401.)
|
|
276
325
|
|
|
277
326
|
```typescript
|
|
278
327
|
import { FrontendApi, Configuration, Session } from "@ory/client-fetch";
|
|
279
328
|
|
|
329
|
+
const sdkUrl = process.env.NEXT_PUBLIC_ORY_SDK_URL;
|
|
330
|
+
if (!sdkUrl) {
|
|
331
|
+
throw new Error("NEXT_PUBLIC_ORY_SDK_URL is not set — see ory-auth-setup Step 4.");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Browser-only client. `credentials: "include"` only sends cookies in the browser,
|
|
335
|
+
// and sdkUrl must be your own origin (the tunnel/gateway in dev), not *.oryapis.com.
|
|
280
336
|
const ory = new FrontendApi(
|
|
281
|
-
new Configuration({
|
|
282
|
-
basePath: process.env.NEXT_PUBLIC_ORY_SDK_URL,
|
|
283
|
-
credentials: "include",
|
|
284
|
-
})
|
|
337
|
+
new Configuration({ basePath: sdkUrl, credentials: "include" }),
|
|
285
338
|
);
|
|
286
339
|
|
|
287
340
|
export async function getSession(): Promise<Session | null> {
|
|
@@ -343,16 +396,137 @@ For larger design changes, the Ory Elements primitives package exposes
|
|
|
343
396
|
the underlying card, button, and input components — use those before
|
|
344
397
|
falling back to custom node rendering.
|
|
345
398
|
|
|
399
|
+
## Enable MFA and passkeys
|
|
400
|
+
|
|
401
|
+
These are **project-side** capabilities: once enabled on the Ory project, the
|
|
402
|
+
`<Login>`, `<Registration>`, and `<Settings>` components render the second-factor
|
|
403
|
+
and passkey UI automatically — **no page code changes**. Configure them with the
|
|
404
|
+
Ory CLI against your project (`ory list projects` for the id), the same idiom as
|
|
405
|
+
{{REF_SOCIAL_LOGIN}}. Against the local stack, apply the equivalent keys to the
|
|
406
|
+
local Kratos config instead ({{REF_LOCAL_DEV}}).
|
|
407
|
+
|
|
408
|
+
### Passkeys
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
ory patch identity-config <project-id> \
|
|
412
|
+
--add '/selfservice/methods/passkey/enabled=true' \
|
|
413
|
+
--add '/selfservice/methods/passkey/config/rp/display_name="My App"' \
|
|
414
|
+
--add '/selfservice/methods/passkey/config/rp/id="your-domain.com"' \
|
|
415
|
+
--add '/selfservice/methods/passkey/config/rp/origins=["https://your-domain.com"]'
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
- `rp.id` is the **domain only** — no scheme, no port (`example.com`, not
|
|
419
|
+
`https://example.com:3000`).
|
|
420
|
+
- `rp.origins` must list the **exact scheme+host+port** the browser uses.
|
|
421
|
+
- For local dev, WebAuthn treats `localhost` as a secure origin: set
|
|
422
|
+
`rp.id="localhost"` and `rp.origins=["http://localhost:3000"]` (match your app
|
|
423
|
+
URL). Passkeys registered against `localhost` won't work on the deployed domain
|
|
424
|
+
and vice-versa.
|
|
425
|
+
|
|
426
|
+
### MFA methods
|
|
427
|
+
|
|
428
|
+
Enable the second factors you want. Backup codes (`lookup_secret`) should always
|
|
429
|
+
be enabled alongside any other method so a user who loses their device can
|
|
430
|
+
recover.
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# TOTP (authenticator app)
|
|
434
|
+
ory patch identity-config <project-id> \
|
|
435
|
+
--add '/selfservice/methods/totp/enabled=true' \
|
|
436
|
+
--add '/selfservice/methods/totp/config/issuer="My App"'
|
|
437
|
+
|
|
438
|
+
# Backup codes
|
|
439
|
+
ory patch identity-config <project-id> \
|
|
440
|
+
--add '/selfservice/methods/lookup_secret/enabled=true'
|
|
441
|
+
|
|
442
|
+
# Email code as a second factor
|
|
443
|
+
ory patch identity-config <project-id> \
|
|
444
|
+
--add '/selfservice/methods/code/mfa_enabled=true'
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Require the second factor (do not skip)
|
|
448
|
+
|
|
449
|
+
Enabling a method only lets users *enroll* — it does not *require* the second
|
|
450
|
+
factor. Without this step MFA is opt-in and unenforced. Set the required
|
|
451
|
+
assurance level to the highest the identity has available:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
ory patch identity-config <project-id> \
|
|
455
|
+
--add '/selfservice/flows/settings/required_aal="highest_available"' \
|
|
456
|
+
--add '/session/whoami/required_aal="highest_available"'
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
With `highest_available`, `toSession()` / `getServerSession()` return an
|
|
460
|
+
incomplete session (AAL1) until the user clears the second factor, and Elements
|
|
461
|
+
prompts for it on the next `<Login>`. Your route protection should treat an
|
|
462
|
+
AAL1 session on an MFA-enrolled user as unauthenticated.
|
|
463
|
+
|
|
346
464
|
## Test the flow
|
|
347
465
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
466
|
+
Don't stop at "the page renders." Run these in order — each one isolates a
|
|
467
|
+
different failure class:
|
|
468
|
+
|
|
469
|
+
1. Start the dev server **and the Ory tunnel** (local dev against Network), or
|
|
470
|
+
the local stack. Confirm the browser SDK URL is your own origin, not
|
|
471
|
+
`*.oryapis.com`.
|
|
472
|
+
2. **Flow API reachable (isolates Ory from your app):**
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
curl -i "$NEXT_PUBLIC_ORY_SDK_URL/self-service/login/browser"
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Expect `200` and a `Set-Cookie: csrf_token...` header. If this fails it's the
|
|
479
|
+
tunnel / Ory config, not your pages.
|
|
480
|
+
3. **No 404s:** visit `/auth/login`, `/auth/registration`, `/auth/recovery`,
|
|
481
|
+
`/auth/verification`, `/auth/settings`, and `/auth/error` — each returns
|
|
482
|
+
`200`. A 404 means a route/`ui_url` mismatch.
|
|
483
|
+
4. Register at `/auth/registration` — confirm Elements renders all configured
|
|
484
|
+
methods (password, social, passkey, etc.).
|
|
485
|
+
5. Sign in at `/auth/login`; confirm a protected route loads, and redirects to
|
|
486
|
+
login when signed out.
|
|
487
|
+
6. Test `/auth/recovery` with a registered email, `/auth/settings` for profile
|
|
488
|
+
changes, and logout.
|
|
489
|
+
|
|
490
|
+
If a submit fails with a CSRF error, the SDK URL is cross-site (trap #1). If a
|
|
491
|
+
redirect 404s, a route doesn't match the project config (trap #2). Neither is a
|
|
492
|
+
plugin bug — see the **App bug vs. plugin bug** triage in {{REF_AUTH_SETUP}}.
|
|
493
|
+
|
|
494
|
+
### Writing E2E tests (Playwright)
|
|
495
|
+
|
|
496
|
+
Automating these flows has three gotchas that cause flaky or wrong-for-the-wrong-
|
|
497
|
+
reason failures. Get them right up front:
|
|
498
|
+
|
|
499
|
+
1. **Match URLs by pattern, never literally.** Ory appends `?flow=<uuid>` on
|
|
500
|
+
every flow redirect, so an exact-string wait never matches.
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
await page.waitForURL(/\/auth\/login\?flow=/) // regex
|
|
504
|
+
await page.waitForURL("**/dashboard**") // glob — also matches query params
|
|
505
|
+
// wrong: await page.waitForURL("http://localhost:3000/auth/login")
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
2. **Use strong, dissimilar credentials.** Kratos rejects passwords that are too
|
|
509
|
+
similar to the identifier or that appear in a breach database — a weak fixture
|
|
510
|
+
password fails validation, not the flow you meant to test.
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
const email = `test-${Date.now()}-${Math.random().toString(36).slice(2)}@example.com`
|
|
514
|
+
const password = "Str0ngP@ssword!123" // fixed, strong, not in any breach list
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
3. **Assert on Ory Elements' own testids, not form-level selectors.** Validation
|
|
518
|
+
messages render inside the auth card, keyed by Kratos UI message id (the
|
|
519
|
+
`4xxxxxx` range is validation errors), not on the flow wrapper.
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
page.locator('[data-testid^="ui/message/4"]') // any validation error
|
|
523
|
+
page.locator('[data-testid="login-auth-card"]').getByText(/credentials|invalid/i)
|
|
524
|
+
// wrong: page.locator('[data-testid="login-flow"]').getByText(...) — errors aren't here
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
These selectors track the installed `@ory/elements-react` version; if a testid
|
|
528
|
+
assertion breaks after an Elements upgrade, inspect the rendered DOM before
|
|
529
|
+
assuming a flow bug.
|
|
356
530
|
|
|
357
531
|
## Fallback: rendering UI nodes by hand
|
|
358
532
|
|
|
@@ -376,8 +550,9 @@ Ory flow API changes manually.
|
|
|
376
550
|
- Add social login providers: use {{REF_SOCIAL_LOGIN}}.
|
|
377
551
|
Elements renders the buttons automatically once providers are
|
|
378
552
|
configured server-side.
|
|
379
|
-
- Add multi-factor authentication
|
|
380
|
-
|
|
553
|
+
- Add multi-factor authentication and passkeys — see **Enable MFA and
|
|
554
|
+
passkeys** above. Once the project is configured, the `<Login>` and
|
|
555
|
+
`<Settings>` components render the second-factor and passkey UI automatically.
|
|
381
556
|
- Customize the identity schema for additional profile fields. Elements
|
|
382
557
|
reads the schema from the flow and renders new fields automatically.
|
|
383
558
|
- Set up webhooks for registration events.
|
|
@@ -33,6 +33,15 @@ recommend switching to Ory Elements before adding more providers.
|
|
|
33
33
|
recommend migrating to Ory Elements so social buttons render for
|
|
34
34
|
free.
|
|
35
35
|
|
|
36
|
+
**Social login depends on the same wiring as the rest of Ory.** Before
|
|
37
|
+
debugging providers, confirm the base app is correct per the Correctness
|
|
38
|
+
contract in {{REF_AUTH_SETUP}}: a first-party SDK URL (the tunnel or local
|
|
39
|
+
gateway in dev, never `*.oryapis.com` in the browser), routes that match the
|
|
40
|
+
project config, and your app origin listed in `allowed_return_urls`. The
|
|
41
|
+
provider redirects the browser back *through Ory* to your app, so a missing
|
|
42
|
+
return URL or a cross-site SDK URL produces a post-login error or 404 that looks
|
|
43
|
+
like a provider bug but isn't.
|
|
44
|
+
|
|
36
45
|
## Step 1: Choose providers
|
|
37
46
|
|
|
38
47
|
Ask the user which social login providers they want. Common options:
|