@ory/argus 0.8.2 → 0.9.1

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 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: "invoke",
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: "invoke",
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 tuples | `createRelationship`, `deleteRelationship` |
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
- Create a shared Ory client configuration. The SDK URL should come
113
- from an environment variable:
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
- NEXT_PUBLIC_ORY_SDK_URL=https://<project-slug>.projects.oryapis.com
132
- # or
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
 
@@ -191,17 +258,27 @@ with the user that an Ory Elements island is not viable.
191
258
 
192
259
  ## Step 7: Add session middleware
193
260
 
194
- Protect authenticated routes by checking the session:
261
+ Protect authenticated routes by checking the session. **Where this code runs
262
+ matters** (contract #3):
195
263
 
196
- ```typescript
197
- const session = await ory.toSession();
198
- if (!session) {
199
- // Redirect to login
200
- }
201
- ```
264
+ - **Browser code** can use the `credentials: "include"` client from Step 4
265
+ directly the browser attaches the session cookie:
266
+
267
+ ```typescript
268
+ const session = await ory.toSession(); // browser only
269
+ if (!session) {
270
+ // Redirect to login
271
+ }
272
+ ```
273
+
274
+ - **Server code** (server components, route handlers, middleware) must forward
275
+ the incoming request's cookies. Do **not** reuse the browser client — it has
276
+ no cookies on the server and will throw (→ 500) or always return 401. Use the
277
+ `@ory/nextjs` server helpers, which read and forward the request cookies for
278
+ you.
202
279
 
203
280
  For Next.js, prefer the official `@ory/nextjs` middleware helper since
204
- it pairs with the Elements pages:
281
+ it pairs with the Elements pages and handles server-side cookie forwarding:
205
282
 
206
283
  ```typescript
207
284
  import { createOryMiddleware } from "@ory/nextjs/middleware";
@@ -216,37 +293,118 @@ export const config = {
216
293
  };
217
294
  ```
218
295
 
219
- ## Step 8: Configure allowed redirect URLs
220
-
221
- Update the Ory project to allow redirects back to your app:
222
-
223
- ```bash
224
- ory patch project <project-id> \
225
- --replace '/services/identity/config/selfservice/allowed_return_urls=["http://localhost:3000", "https://your-domain.com"]'
226
- ```
227
-
228
- ## Step 9: Set up the Ory Tunnel for local development
229
-
230
- For local development, use the Ory tunnel to proxy requests and handle cookies:
231
-
232
- ```bash
233
- ory tunnel http://localhost:3000 --project <project-slug>
234
- ```
235
-
236
- This runs a proxy on `http://localhost:4000` that handles cookie domains
237
- correctly for local development. Update your SDK URL to point to the
238
- tunnel during development.
239
-
240
- ## Step 10: Verify the setup
241
-
242
- 1. Start the development server
243
- 2. Navigate to the login page — confirm Ory Elements renders the form,
244
- including any social login buttons configured on the project
245
- 3. Create a test account via the registration page
246
- 4. Verify login works
247
- 5. Test account recovery flow
248
- 6. Test the verification flow
249
- 7. Check that protected routes redirect unauthenticated users
296
+ ## Step 8: Align the project's routes and return URLs with your app
297
+
298
+ This step prevents the 404s in contract #2. Ory redirects the browser to the
299
+ self-service UI URLs it has configured; those must point at pages your app
300
+ actually serves.
301
+
302
+ **Pick one route convention and use it everywhere.** This guide uses
303
+ `/auth/login`, `/auth/registration`, `/auth/recovery`, `/auth/verification`,
304
+ `/auth/settings`, and `/auth/error`. The page files, the middleware path lists,
305
+ and the project config below must all use the same paths.
306
+
307
+ 1. **Create an error page.** Ory redirects to the error UI on *any* flow error;
308
+ if it doesn't exist you get a 404 (or a blank page) instead of a readable
309
+ message. Create `app/auth/error/page.tsx` rendering the Elements error
310
+ component (`<Error>` / the flow-error view for your installed version).
311
+
312
+ 2. **Tell Ory where the pages are.**
313
+ - **Next.js with `@ory/nextjs`:** declare the UI paths in its config (the
314
+ UI-path overrides in `ory.config.ts`) so its helpers, middleware, and proxy
315
+ route to your pages. The SDK serves the UI under your own origin, so you
316
+ usually do not also edit the project's `ui_url`s.
317
+ - **React SPA / no `@ory/nextjs` proxy:** set the project's self-service UI
318
+ URLs to your app's routes:
319
+
320
+ ```bash
321
+ ory patch project <project-id> \
322
+ --replace '/services/identity/config/selfservice/flows/login/ui_url="https://your-domain.com/auth/login"' \
323
+ --replace '/services/identity/config/selfservice/flows/registration/ui_url="https://your-domain.com/auth/registration"' \
324
+ --replace '/services/identity/config/selfservice/flows/recovery/ui_url="https://your-domain.com/auth/recovery"' \
325
+ --replace '/services/identity/config/selfservice/flows/verification/ui_url="https://your-domain.com/auth/verification"' \
326
+ --replace '/services/identity/config/selfservice/flows/settings/ui_url="https://your-domain.com/auth/settings"' \
327
+ --replace '/services/identity/config/selfservice/flows/error/ui_url="https://your-domain.com/auth/error"'
328
+ ```
329
+
330
+ 3. **Allow redirects back to your app** (every origin you run on — dev and prod):
331
+
332
+ ```bash
333
+ ory patch project <project-id> \
334
+ --replace '/services/identity/config/selfservice/allowed_return_urls=["http://localhost:3000", "https://your-domain.com"]'
335
+ ```
336
+
337
+ ## Step 9: Serve Ory first-party (required — contract #1)
338
+
339
+ The browser must reach Ory on the same site as your app, or cookie-based flows
340
+ fail CSRF. How you achieve that depends on the environment:
341
+
342
+ - **Local dev against Ory Network — run the Ory Tunnel.** This is required, not
343
+ optional:
344
+
345
+ ```bash
346
+ ory tunnel http://localhost:3000 --project <project-slug>
347
+ ```
348
+
349
+ It runs a proxy on `http://localhost:4000` that serves Ory first-party to
350
+ `localhost`. Point `NEXT_PUBLIC_ORY_SDK_URL` at the tunnel
351
+ (`http://localhost:4000`) — **not** at `https://<slug>.projects.oryapis.com`.
352
+
353
+ - **Local dev against the local stack** — the gateway is already first-party on
354
+ `http://localhost:4000`; no tunnel needed (switch to {{REF_LOCAL_DEV}}).
355
+
356
+ - **Production** — serve Ory from your own domain via an Ory Network custom
357
+ domain (CNAME) or the `@ory/nextjs` proxy, and set the browser SDK URL to that
358
+ same-origin path.
359
+
360
+ If you skip this step, login will appear to work but `whoami`/`toSession` will
361
+ return 401 and form submits will fail with a CSRF error — the classic symptom of
362
+ a cross-site SDK URL.
363
+
364
+ ## Step 10: Verify the setup — and confirm it's correct, not just present
365
+
366
+ Do not declare success on "the pages render." Run these checks; each maps to a
367
+ contract item so a failure points straight at the cause.
368
+
369
+ 1. **SDK URL is first-party (contract #1).** Confirm the browser SDK URL is your
370
+ own origin (the tunnel/gateway in dev), not `*.oryapis.com`. In the browser
371
+ devtools Network tab, the flow requests should go to your origin and the
372
+ response should `Set-Cookie` a CSRF cookie.
373
+ 2. **The flow API itself works (isolates Ory from your app).** Against the URL
374
+ the browser uses:
375
+
376
+ ```bash
377
+ curl -i "$NEXT_PUBLIC_ORY_SDK_URL/self-service/login/browser"
378
+ ```
379
+
380
+ Expect `200` with a `Set-Cookie: csrf_token...` header. If this fails, the
381
+ problem is Ory config / the tunnel — not your app code.
382
+ 3. **Every redirect target exists (contract #2).** Visit `/auth/login`,
383
+ `/auth/registration`, `/auth/recovery`, `/auth/verification`,
384
+ `/auth/settings`, and `/auth/error` directly. Each must return `200`, not
385
+ `404`. A 404 here means a route/`ui_url` mismatch (Step 8).
386
+ 4. **End-to-end:** register a test account, log in, confirm a protected route
387
+ loads while signed in and redirects to login when signed out, then test
388
+ recovery and verification.
389
+
390
+ ### App bug vs. plugin bug
391
+
392
+ These are two different systems. Keep them straight so issues are filed in the
393
+ right place:
394
+
395
+ - **The generated app** (login pages, CSRF, sessions, the `/auth/*` routes) is
396
+ ordinary code running in the user's project. A `404`/`500`/CSRF on an `/auth/*`
397
+ route, or in the browser Network tab, is an **app/config** issue — work the
398
+ four contract items and the checks above.
399
+ - **The agent plugin** governs *this coding session* — it authenticates the
400
+ agent, checks Ory Permissions before each tool call, and writes trace spans. It
401
+ never serves your app's HTTP routes. Diagnose it with
402
+ `{{NPX}} status` and the debug log (`ORY_AGENT_DEBUG=true`), **not** by looking
403
+ at your app's auth pages.
404
+
405
+ Quick triage: if `curl` to the flow API (check 2) succeeds but the app page
406
+ fails, it's the app. If `curl` fails, it's Ory/tunnel config. Neither is the
407
+ plugin unless `{{NPX}} status` reports a problem.
250
408
 
251
409
  ## Customization
252
410
 
@@ -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:
@@ -270,18 +291,39 @@ Initialize with `createBrowserSettingsFlow` and render
270
291
  component handles password changes, profile traits, MFA enrollment,
271
292
  and connected social providers.
272
293
 
294
+ ## Build the error page (do not skip — missing it causes 404s)
295
+
296
+ Ory redirects the browser to the error UI on **any** flow error (expired flow,
297
+ validation failure, misconfiguration). If that route doesn't exist the user
298
+ hits a 404 or a blank page instead of a readable message, and the failure looks
299
+ like a plugin bug when it's just a missing page.
300
+
301
+ Create `app/auth/error/page.tsx` and render the Elements error view for your
302
+ installed version (`getFlowError` from `@ory/nextjs/app` + the Elements error
303
+ component). Make sure its path matches the project's `error.ui_url`
304
+ (see {{REF_AUTH_SETUP}}, Step 8). For a React SPA, fetch the error with
305
+ `getFlowError({ id })` from the `error` query param and display
306
+ `error.error.message`.
307
+
273
308
  ## Add session management
274
309
 
275
- Create a utility to check the current session:
310
+ Create a **browser** utility to check the current session. (For server
311
+ components, route handlers, and middleware, use the `@ory/nextjs` server
312
+ helpers instead — they forward the request cookies. Reusing this browser client
313
+ on the server has no cookies and will 500/401.)
276
314
 
277
315
  ```typescript
278
316
  import { FrontendApi, Configuration, Session } from "@ory/client-fetch";
279
317
 
318
+ const sdkUrl = process.env.NEXT_PUBLIC_ORY_SDK_URL;
319
+ if (!sdkUrl) {
320
+ throw new Error("NEXT_PUBLIC_ORY_SDK_URL is not set — see ory-auth-setup Step 4.");
321
+ }
322
+
323
+ // Browser-only client. `credentials: "include"` only sends cookies in the browser,
324
+ // and sdkUrl must be your own origin (the tunnel/gateway in dev), not *.oryapis.com.
280
325
  const ory = new FrontendApi(
281
- new Configuration({
282
- basePath: process.env.NEXT_PUBLIC_ORY_SDK_URL,
283
- credentials: "include",
284
- })
326
+ new Configuration({ basePath: sdkUrl, credentials: "include" }),
285
327
  );
286
328
 
287
329
  export async function getSession(): Promise<Session | null> {
@@ -345,14 +387,33 @@ falling back to custom node rendering.
345
387
 
346
388
  ## Test the flow
347
389
 
348
- 1. Start the dev server and Ory tunnel (if developing locally)
349
- 2. Visit `/auth/registration` to create an account — confirm Elements
350
- renders all configured methods (password, social, passkey, etc.)
351
- 3. Visit `/auth/login` to sign in
352
- 4. Verify session is established (check protected routes)
353
- 5. Test `/auth/recovery` with a registered email
354
- 6. Test `/auth/settings` for profile changes
355
- 7. Test logout
390
+ Don't stop at "the page renders." Run these in order each one isolates a
391
+ different failure class:
392
+
393
+ 1. Start the dev server **and the Ory tunnel** (local dev against Network), or
394
+ the local stack. Confirm the browser SDK URL is your own origin, not
395
+ `*.oryapis.com`.
396
+ 2. **Flow API reachable (isolates Ory from your app):**
397
+
398
+ ```bash
399
+ curl -i "$NEXT_PUBLIC_ORY_SDK_URL/self-service/login/browser"
400
+ ```
401
+
402
+ Expect `200` and a `Set-Cookie: csrf_token...` header. If this fails it's the
403
+ tunnel / Ory config, not your pages.
404
+ 3. **No 404s:** visit `/auth/login`, `/auth/registration`, `/auth/recovery`,
405
+ `/auth/verification`, `/auth/settings`, and `/auth/error` — each returns
406
+ `200`. A 404 means a route/`ui_url` mismatch.
407
+ 4. Register at `/auth/registration` — confirm Elements renders all configured
408
+ methods (password, social, passkey, etc.).
409
+ 5. Sign in at `/auth/login`; confirm a protected route loads, and redirects to
410
+ login when signed out.
411
+ 6. Test `/auth/recovery` with a registered email, `/auth/settings` for profile
412
+ changes, and logout.
413
+
414
+ If a submit fails with a CSRF error, the SDK URL is cross-site (trap #1). If a
415
+ redirect 404s, a route doesn't match the project config (trap #2). Neither is a
416
+ plugin bug — see the **App bug vs. plugin bug** triage in {{REF_AUTH_SETUP}}.
356
417
 
357
418
  ## Fallback: rendering UI nodes by hand
358
419
 
@@ -86,7 +86,7 @@ import {
86
86
  const client = OryAgentClient.fromEnv("my-agent");
87
87
  const { projectUrl } = resolveConfig();
88
88
 
89
- // 1. User gate — interactive PKCE when ORY_USER_LOGIN=1, no-op otherwise.
89
+ // 1. User gate — interactive PKCE when ORY_USER_LOGIN=true, no-op otherwise.
90
90
  const userDecision = await ensureUserAuthenticated(client, {
91
91
  binName: "my-agent",
92
92
  harness: "my-agent",
@@ -409,7 +409,7 @@ PKCE flow, permission tuples, and trace spans are all visible:
409
409
  1. {{REF_LOCAL_UP}} — brings up Kratos / Keto / Hydra on `localhost:4000`
410
410
  and seeds a demo user. The banner prints the email + password.
411
411
  2. Export the env vars the launcher writes (`ORY_PROJECT_URL`,
412
- `ORY_USER_LOGIN=1`, `ORY_OAUTH2_CLIENT_ID`, optional
412
+ `ORY_USER_LOGIN=true`, `ORY_OAUTH2_CLIENT_ID`, optional
413
413
  `ORY_AGENT_TRACE_FILE` for an NDJSON span log).
414
414
  3. Start your agent. Confirm the browser opens for PKCE login.
415
415
  4. Invoke a gated tool and `tail -f $ORY_AGENT_TRACE_FILE | jq .` — you
@@ -66,7 +66,7 @@ export const template = Template()
66
66
  // Sandbox runtime defaults. Per-tenant secrets (project URL, tokens, client
67
67
  // IDs) MUST be passed at Sandbox.create() time, never baked into the image.
68
68
  .setEnvs({
69
- ORY_USER_LOGIN: "1",
69
+ ORY_USER_LOGIN: "true",
70
70
  ORY_PERMISSION_MODE: "observe",
71
71
  ORY_PERMISSION_NAMESPACE: "AgentTools",
72
72
  ORY_AGENT_DEBUG: "true",
@@ -41,9 +41,11 @@ What you want to see:
41
41
  If any of those are wrong, fix them before continuing:
42
42
 
43
43
  ```sh
44
- {{NPX}} configure --project-url <URL> --api-key <KEY>
44
+ {{NPX}} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]
45
45
  ```
46
46
 
47
+ `--oauth2-client-id` is the public OAuth2 client registered in your Ory project (required when `ORY_USER_LOGIN=true`; see the *Register the user OAuth2 client* section of the plugin README).
48
+
47
49
  ## Step 2: Look at the current permission posture
48
50
 
49
51
  ```sh
@@ -94,7 +96,7 @@ Two common failure modes:
94
96
  1. **No user identity cached.** Bootstrap needs to know which subject
95
97
  to grant tuples to. If user login has never run (no PKCE login,
96
98
  no `ORY_USER_SUBJECT_ID`), the command refuses. Run the harness once
97
- with `ORY_USER_LOGIN=1` to cache a user token, or set
99
+ with `ORY_USER_LOGIN=true` to cache a user token, or set
98
100
  `ORY_USER_SUBJECT_ID=<id>` to target a known subject.
99
101
  2. **Credentials lack write scope on the permission namespace.** The
100
102
  command prints the full tuple list so you can apply them manually
@@ -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:
@@ -290,7 +290,7 @@ async function resolveAgentCredentials(options = {}) {
290
290
  }
291
291
  return {
292
292
  kind: "none",
293
- reason: "No agent credentials configured. Run with a user session (ORY_USER_LOGIN=1) or set ORY_AGENT_API_KEY / ORY_AGENT_CLIENT_ID + ORY_AGENT_CLIENT_SECRET / ORY_AGENT_REGISTRATION_TOKEN.",
293
+ reason: "No agent credentials configured. Run with a user session (ORY_USER_LOGIN=true) or set ORY_AGENT_API_KEY / ORY_AGENT_CLIENT_ID + ORY_AGENT_CLIENT_SECRET / ORY_AGENT_REGISTRATION_TOKEN.",
294
294
  warnings,
295
295
  };
296
296
  }
package/dist/cli.d.ts CHANGED
@@ -1,8 +1,16 @@
1
1
  /**
2
2
  * Shared `configure` command implementation for all plugin CLIs.
3
3
  *
4
- * Parses --project-url and --api-key from args. If neither is provided,
5
- * shows the current configuration. Otherwise saves the provided values.
4
+ * Parses --project-url, --api-key, and --oauth2-client-id from args.
5
+ * If none is provided, shows the current configuration. Otherwise saves
6
+ * the provided values.
7
+ *
8
+ * When `--project-url` is provided, requires the OAuth2 client id to be
9
+ * resolvable from one of: the `--oauth2-client-id` flag, the
10
+ * `ORY_OAUTH2_CLIENT_ID` env var, or an already-persisted value in the
11
+ * shared config. The PKCE browser flow that runs when
12
+ * `ORY_USER_LOGIN=true` can't self-register that client — surfacing the
13
+ * requirement here keeps users from a silently-broken setup later.
6
14
  */
7
15
  export declare function runConfigureCommand(binName: string, args: string[]): void;
8
16
  /**
package/dist/cli.js CHANGED
@@ -54,12 +54,21 @@ const tracer_js_1 = require("./tracer.js");
54
54
  /**
55
55
  * Shared `configure` command implementation for all plugin CLIs.
56
56
  *
57
- * Parses --project-url and --api-key from args. If neither is provided,
58
- * shows the current configuration. Otherwise saves the provided values.
57
+ * Parses --project-url, --api-key, and --oauth2-client-id from args.
58
+ * If none is provided, shows the current configuration. Otherwise saves
59
+ * the provided values.
60
+ *
61
+ * When `--project-url` is provided, requires the OAuth2 client id to be
62
+ * resolvable from one of: the `--oauth2-client-id` flag, the
63
+ * `ORY_OAUTH2_CLIENT_ID` env var, or an already-persisted value in the
64
+ * shared config. The PKCE browser flow that runs when
65
+ * `ORY_USER_LOGIN=true` can't self-register that client — surfacing the
66
+ * requirement here keeps users from a silently-broken setup later.
59
67
  */
60
68
  function runConfigureCommand(binName, args) {
61
69
  let projectUrl;
62
70
  let apiKey;
71
+ let oauth2ClientId;
63
72
  let auditOnly = false;
64
73
  for (let i = 0; i < args.length; i++) {
65
74
  switch (args[i]) {
@@ -69,30 +78,35 @@ function runConfigureCommand(binName, args) {
69
78
  case "--api-key":
70
79
  apiKey = args[++i];
71
80
  break;
81
+ case "--oauth2-client-id":
82
+ oauth2ClientId = args[++i];
83
+ break;
72
84
  case "--audit-only":
73
85
  auditOnly = true;
74
86
  break;
75
87
  }
76
88
  }
77
- if (!projectUrl && !apiKey && !auditOnly) {
89
+ if (!projectUrl && !apiKey && !oauth2ClientId && !auditOnly) {
78
90
  const resolved = (0, config_js_1.resolveConfig)();
79
91
  const configPath = (0, config_js_1.getConfigPath)();
80
92
  console.log("Ory Plugin Configuration");
81
93
  console.log("========================");
82
94
  console.log("");
83
- console.log(`Config file: ${configPath}`);
84
- console.log(`Project URL: ${resolved.projectUrl ?? "(not set)"} [${resolved.projectUrlSource}]`);
85
- console.log(`API Key: ${resolved.apiKey ? "(set)" : "(not set)"} [${resolved.apiKeySource}]`);
86
- console.log(`Audit Only: ${resolved.auditOnly ? "yes" : "no"}`);
95
+ console.log(`Config file: ${configPath}`);
96
+ console.log(`Project URL: ${resolved.projectUrl ?? "(not set)"} [${resolved.projectUrlSource}]`);
97
+ console.log(`API Key: ${resolved.apiKey ? "(set)" : "(not set)"} [${resolved.apiKeySource}]`);
98
+ console.log(`OAuth2 Client ID: ${resolved.oauth2ClientId ?? "(not set)"} [${resolved.oauth2ClientIdSource}]`);
99
+ console.log(`Audit Only: ${resolved.auditOnly ? "yes" : "no"}`);
87
100
  console.log("");
88
101
  console.log("To configure:");
89
- console.log(` npx ${binName} configure --project-url <URL> --api-key <KEY>`);
102
+ console.log(` npx ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
90
103
  console.log("");
91
104
  console.log("To enable audit logging only (no auth or permission checks):");
92
105
  console.log(` npx ${binName} configure --audit-only`);
93
106
  console.log("");
94
107
  console.log("Or set environment variables:");
95
108
  console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
109
+ console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
96
110
  console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
97
111
  return;
98
112
  }
@@ -106,11 +120,49 @@ function runConfigureCommand(binName, args) {
106
120
  console.log("This configuration is shared across all Ory agent plugins.");
107
121
  return;
108
122
  }
123
+ // When --project-url is provided we need a usable OAuth2 client id for
124
+ // the user PKCE flow — block the configure with a clear error if none
125
+ // is reachable from the flag, env, or already-persisted config.
126
+ if (projectUrl) {
127
+ const persisted = (0, config_js_1.loadConfig)().oauth2ClientId;
128
+ const envClientId = process.env.ORY_OAUTH2_CLIENT_ID;
129
+ const resolved = oauth2ClientId ?? envClientId ?? persisted;
130
+ if (!resolved) {
131
+ console.error("Error: --project-url requires an OAuth2 client id for the user PKCE flow.\n" +
132
+ "\n" +
133
+ "Provide one with --oauth2-client-id <id>, or set ORY_OAUTH2_CLIENT_ID in your\n" +
134
+ "environment. The client must be a public OAuth2 client (no client secret,\n" +
135
+ "token_endpoint_auth_method=none) registered in your Ory project with all four\n" +
136
+ "loopback redirect URIs:\n" +
137
+ " http://127.0.0.1:47823/callback\n" +
138
+ " http://127.0.0.1:47824/callback\n" +
139
+ " http://127.0.0.1:47825/callback\n" +
140
+ " http://127.0.0.1:47826/callback\n" +
141
+ "\n" +
142
+ "Create it with:\n" +
143
+ " ory create oauth2-client --project <project-id> \\\n" +
144
+ " --name \"ory-agent-plugin\" \\\n" +
145
+ " --grant-type authorization_code,refresh_token \\\n" +
146
+ " --response-type code \\\n" +
147
+ " --scope openid,offline_access \\\n" +
148
+ " --token-endpoint-auth-method none \\\n" +
149
+ " --redirect-uri http://127.0.0.1:47823/callback \\\n" +
150
+ " --redirect-uri http://127.0.0.1:47824/callback \\\n" +
151
+ " --redirect-uri http://127.0.0.1:47825/callback \\\n" +
152
+ " --redirect-uri http://127.0.0.1:47826/callback\n" +
153
+ "\n" +
154
+ "If you only want audit logging (no auth or permission checks), use:\n" +
155
+ ` npx ${binName} configure --audit-only`);
156
+ process.exit(1);
157
+ }
158
+ }
109
159
  const update = {};
110
160
  if (projectUrl)
111
161
  update.projectUrl = projectUrl;
112
162
  if (apiKey)
113
163
  update.apiKey = apiKey;
164
+ if (oauth2ClientId)
165
+ update.oauth2ClientId = oauth2ClientId;
114
166
  // Clear auditOnly when configuring with a project URL
115
167
  update.auditOnly = false;
116
168
  (0, config_js_1.saveConfig)(update);
@@ -120,9 +172,11 @@ function runConfigureCommand(binName, args) {
120
172
  console.log(` Project URL: ${projectUrl}`);
121
173
  if (apiKey)
122
174
  console.log(` API Key: (set)`);
175
+ if (oauth2ClientId)
176
+ console.log(` OAuth2 Client ID: ${oauth2ClientId}`);
123
177
  console.log("");
124
178
  console.log("This configuration is shared across all Ory agent plugins.");
125
- console.log("Environment variables (ORY_PROJECT_URL, ORY_AGENT_API_KEY) take precedence when set.");
179
+ console.log("Environment variables (ORY_PROJECT_URL, ORY_AGENT_API_KEY, ORY_OAUTH2_CLIENT_ID) take precedence when set.");
126
180
  }
127
181
  /**
128
182
  * Print the "Configuration:" block showing Ory project URL and API key status.
@@ -136,6 +190,7 @@ function printOryConfig() {
136
190
  console.log(` Mode: ${resolved.auditOnly ? "audit-only" : "full"}`);
137
191
  console.log(` Project URL: ${resolved.projectUrl ?? "NOT SET"} [source: ${resolved.projectUrlSource}]`);
138
192
  console.log(` API Key: ${resolved.apiKey ? "(set)" : "NOT SET"} [source: ${resolved.apiKeySource}]`);
193
+ console.log(` OAuth2 Client: ${resolved.oauth2ClientId ?? "NOT SET"} [source: ${resolved.oauth2ClientIdSource}]`);
139
194
  console.log(` Permission mode: ${resolved.permissionMode} [source: ${resolved.permissionModeSource}]`);
140
195
  console.log(` Namespace: ${namespace}`);
141
196
  }
@@ -153,6 +208,7 @@ function printEnvironment() {
153
208
  false,
154
209
  ],
155
210
  ["ORY_AGENT_CLIENT_ID", process.env.ORY_AGENT_CLIENT_ID, false],
211
+ ["ORY_OAUTH2_CLIENT_ID", process.env.ORY_OAUTH2_CLIENT_ID, false],
156
212
  [
157
213
  "ORY_USER_SESSION_TOKEN",
158
214
  process.env.ORY_USER_SESSION_TOKEN ? "(set)" : undefined,
@@ -210,10 +266,13 @@ function printEnvHelp(binName) {
210
266
  console.log("Configure your Ory credentials (one of these methods):");
211
267
  console.log("");
212
268
  console.log(" Option 1 — Save to config file (persists across sessions):");
213
- console.log(` npx ${binName} configure --project-url https://your-project.projects.oryapis.com --api-key ory_pat_...`);
269
+ console.log(` npx ${binName} configure --project-url https://your-project.projects.oryapis.com \\`);
270
+ console.log(" --oauth2-client-id <public OAuth2 client id> \\");
271
+ console.log(" --api-key ory_pat_...");
214
272
  console.log("");
215
273
  console.log(" Option 2 — Set environment variables:");
216
274
  console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
275
+ console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
217
276
  console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
218
277
  console.log("");
219
278
  console.log("If neither is set when a session starts, the agent will prompt for configuration.");
@@ -371,7 +430,7 @@ async function interactiveConfigPrompt(binName) {
371
430
  "Run one of the following commands in another terminal:",
372
431
  "",
373
432
  " Connect to Ory (enables auth & permission checks):",
374
- ` npx ${binName} configure --project-url <URL> --api-key <KEY>`,
433
+ ` npx ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`,
375
434
  "",
376
435
  " Or, enable audit logging only (no auth or permission checks):",
377
436
  ` npx ${binName} configure --audit-only`,
package/dist/config.d.ts CHANGED
@@ -75,6 +75,16 @@ export type PermissionMode = "observe" | "enforce";
75
75
  export interface OryPluginConfig {
76
76
  projectUrl?: string;
77
77
  apiKey?: string;
78
+ /**
79
+ * Public OAuth2 client id used by the user PKCE browser flow when
80
+ * `ORY_USER_LOGIN` is on. The client must be registered ahead of time
81
+ * in the Ory project with all four loopback redirect URIs
82
+ * (`http://127.0.0.1:47823..47826/callback`) and
83
+ * `token_endpoint_auth_method=none`. The local stack provisions one
84
+ * automatically; against a hosted project the operator registers it
85
+ * once and supplies the id here (or via `ORY_OAUTH2_CLIENT_ID`).
86
+ */
87
+ oauth2ClientId?: string;
78
88
  /** When true, only audit logging is enabled — no auth or permission checks. */
79
89
  auditOnly?: boolean;
80
90
  /**
@@ -144,11 +154,13 @@ export declare function mutateConfig(mutator: (current: OryPluginConfig) => OryP
144
154
  export declare function resolveConfig(): {
145
155
  projectUrl?: string;
146
156
  apiKey?: string;
157
+ oauth2ClientId?: string;
147
158
  auditOnly: boolean;
148
159
  permissionMode: PermissionMode;
149
160
  permissionModeSource: "env" | "config" | "default";
150
161
  projectUrlSource: "env" | "config" | "none";
151
162
  apiKeySource: "env" | "config" | "none";
163
+ oauth2ClientIdSource: "env" | "config" | "none";
152
164
  };
153
165
  /**
154
166
  * Build the "not configured" prompt message for a given harness CLI bin name.
package/dist/config.js CHANGED
@@ -115,6 +115,7 @@ function loadConfig() {
115
115
  return {
116
116
  projectUrl: typeof parsed.projectUrl === "string" ? parsed.projectUrl : undefined,
117
117
  apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : undefined,
118
+ oauth2ClientId: typeof parsed.oauth2ClientId === "string" ? parsed.oauth2ClientId : undefined,
118
119
  auditOnly: parsed.auditOnly === true ? true : undefined,
119
120
  permissionMode: parsePermissionMode(parsed.permissionMode),
120
121
  user: parseUserCredentials(parsed),
@@ -328,6 +329,7 @@ function resolveConfig() {
328
329
  // alias kept for back-compat (agent-auth emits a warning when only the
329
330
  // legacy form is set).
330
331
  const envApiKey = process.env.ORY_AGENT_API_KEY ?? process.env.ORY_API_KEY;
332
+ const envClientId = process.env.ORY_OAUTH2_CLIENT_ID;
331
333
  const envMode = parsePermissionMode(process.env.ORY_PERMISSION_MODE);
332
334
  const permissionMode = envMode ?? file.permissionMode ?? "observe";
333
335
  const permissionModeSource = envMode
@@ -338,11 +340,13 @@ function resolveConfig() {
338
340
  return {
339
341
  projectUrl: envProjectUrl ?? file.projectUrl,
340
342
  apiKey: envApiKey ?? file.apiKey,
343
+ oauth2ClientId: envClientId ?? file.oauth2ClientId,
341
344
  auditOnly: file.auditOnly === true,
342
345
  permissionMode,
343
346
  permissionModeSource,
344
347
  projectUrlSource: envProjectUrl ? "env" : file.projectUrl ? "config" : "none",
345
348
  apiKeySource: envApiKey ? "env" : file.apiKey ? "config" : "none",
349
+ oauth2ClientIdSource: envClientId ? "env" : file.oauth2ClientId ? "config" : "none",
346
350
  };
347
351
  }
348
352
  /**
@@ -352,7 +356,7 @@ function configPromptMessage(binName) {
352
356
  return ("The Ory agent plugin is installed but not yet configured.\n" +
353
357
  "\n" +
354
358
  " Option 1 — Connect to Ory (enables authentication and permission checks):\n" +
355
- ` npx ${binName} configure --project-url <URL> --api-key <KEY>\n` +
359
+ ` npx ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]\n` +
356
360
  "\n" +
357
361
  " Option 2 — Continue without authentication (audit logging only):\n" +
358
362
  ` npx ${binName} configure --audit-only\n` +
package/dist/dev.js CHANGED
@@ -396,7 +396,7 @@ function buildLocalOryEnv(gatewayUrl, seed) {
396
396
  ORY_PERMISSION_NAMESPACE: seed.permissions.namespace,
397
397
  // User login — always on in local dev so the launcher demonstrates
398
398
  // the interactive PKCE login UX end-to-end every session.
399
- ORY_USER_LOGIN: "1",
399
+ ORY_USER_LOGIN: "true",
400
400
  // Agent identity — the harness self-registers via DCR on first run
401
401
  // using the user's bearer as the initial access token. No static
402
402
  // client_credentials are seeded; explicitly clear them so a leaking
@@ -622,8 +622,17 @@ function localEnv() {
622
622
  }
623
623
  function localConfigure() {
624
624
  console.log(`Saving local Ory gateway URL to shared config...`);
625
- (0, config_js_1.saveConfig)({ projectUrl: configs_js_1.GATEWAY_URL, auditOnly: false });
626
- console.log(` Project URL: ${configs_js_1.GATEWAY_URL}`);
625
+ // Persist the seeded user PKCE client id alongside the project URL so
626
+ // a subsequent `configure --project-url <hosted>` doesn't trip the
627
+ // require-OAuth2-client check, and the user gate resolves the right
628
+ // client out of the box.
629
+ (0, config_js_1.saveConfig)({
630
+ projectUrl: configs_js_1.GATEWAY_URL,
631
+ oauth2ClientId: seed_js_1.USER_CLIENT_ID,
632
+ auditOnly: false,
633
+ });
634
+ console.log(` Project URL: ${configs_js_1.GATEWAY_URL}`);
635
+ console.log(` OAuth2 Client ID: ${seed_js_1.USER_CLIENT_ID}`);
627
636
  console.log("");
628
637
  console.log("All Ory agent plugins will now connect to the local environment.");
629
638
  console.log("This persists across sessions. Run 'configure --project-url <URL>'");
@@ -647,7 +656,7 @@ function printSeedResult(result) {
647
656
  console.log("To use with any Ory agent plugin, set these environment variables:");
648
657
  console.log("");
649
658
  console.log(` export ORY_PROJECT_URL=${configs_js_1.GATEWAY_URL}`);
650
- console.log(` export ORY_USER_LOGIN=1`);
659
+ console.log(` export ORY_USER_LOGIN=true`);
651
660
  console.log(` export ORY_OAUTH2_CLIENT_ID=${result.user.client.clientId}`);
652
661
  console.log(` export ORY_USER_SUBJECT_NAMESPACE=User`);
653
662
  console.log(` export ORY_USER_SUBJECT_ID=${result.user.identity.id}`);
@@ -23,6 +23,13 @@
23
23
  * Idempotent — re-running the seed reuses existing identities and
24
24
  * recreates OAuth2 clients (so the secret is always known).
25
25
  */
26
+ /**
27
+ * Stable client id Hydra issues to the seeded user PKCE client. Stable
28
+ * so `local configure` can persist it to the shared config and downstream
29
+ * runs can rely on `ORY_OAUTH2_CLIENT_ID` resolution without first
30
+ * dragging the value out of the `local up` banner.
31
+ */
32
+ export declare const USER_CLIENT_ID = "ory-user-local";
26
33
  export interface SeededIdentity {
27
34
  id: string;
28
35
  email: string;
@@ -25,7 +25,7 @@
25
25
  * recreates OAuth2 clients (so the secret is always known).
26
26
  */
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.USER_SUBJECT_NAMESPACE = void 0;
28
+ exports.USER_SUBJECT_NAMESPACE = exports.USER_CLIENT_ID = void 0;
29
29
  exports.seedLocalEnvironment = seedLocalEnvironment;
30
30
  const auth_js_1 = require("../auth.js");
31
31
  const tool_catalog_js_1 = require("../tool-catalog.js");
@@ -38,7 +38,13 @@ const AGENT_PASSWORD = "ory-agent-local-dev-password!";
38
38
  const USER_EMAIL = "user@ory-local.dev";
39
39
  const USER_PASSWORD = "ory-user-local-dev-password!";
40
40
  const USER_CLIENT_NAME = "ory-agent-plugins-local-user";
41
- const USER_CLIENT_ID = "ory-user-local";
41
+ /**
42
+ * Stable client id Hydra issues to the seeded user PKCE client. Stable
43
+ * so `local configure` can persist it to the shared config and downstream
44
+ * runs can rely on `ORY_OAUTH2_CLIENT_ID` resolution without first
45
+ * dragging the value out of the `local up` banner.
46
+ */
47
+ exports.USER_CLIENT_ID = "ory-user-local";
42
48
  /**
43
49
  * Tools the dev launcher seeds tuples for. Sourced from the shared
44
50
  * per-harness catalog so a single seed grants the local user identity
@@ -193,11 +199,11 @@ async function seedLocalEnvironment(namespace = "AgentTools") {
193
199
  // OAuth2 client is intentionally NOT pre-registered; the harness
194
200
  // will self-register via DCR on first session start.
195
201
  process.stderr.write(" Registering user OAuth2 client (PKCE)...\n");
196
- let userClient = { clientId: USER_CLIENT_ID };
202
+ let userClient = { clientId: exports.USER_CLIENT_ID };
197
203
  try {
198
204
  userClient = await ensureOAuth2Client({
199
205
  clientName: USER_CLIENT_NAME,
200
- clientId: USER_CLIENT_ID,
206
+ clientId: exports.USER_CLIENT_ID,
201
207
  // Public PKCE client — no secret. Hydra accepts an empty string
202
208
  // for `token_endpoint_auth_method=none`.
203
209
  grantTypes: ["authorization_code", "refresh_token"],
@@ -129,12 +129,12 @@ async function runPermissionsStatus(binName, harness) {
129
129
  console.log("");
130
130
  if (resolved.auditOnly) {
131
131
  console.log("Ory is disabled (audit-only). No permission checks run.");
132
- console.log(`Re-enable with: ${binName} configure --project-url <URL> --api-key <KEY>`);
132
+ console.log(`Re-enable with: ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
133
133
  return 0;
134
134
  }
135
135
  if (!resolved.projectUrl) {
136
136
  console.log("No project URL configured — cannot probe permission coverage.");
137
- console.log(`Configure first: ${binName} configure --project-url <URL> --api-key <KEY>`);
137
+ console.log(`Configure first: ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
138
138
  return 0;
139
139
  }
140
140
  if (catalog.length === 0) {
@@ -211,12 +211,12 @@ async function runPermissionsBootstrap(binName, harness, args) {
211
211
  const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
212
212
  if (resolved.auditOnly) {
213
213
  console.error("Ory is in audit-only mode (kill-switch). Nothing to bootstrap.");
214
- console.error(`Re-enable Ory: ${binName} configure --project-url <URL> --api-key <KEY>`);
214
+ console.error(`Re-enable Ory: ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
215
215
  return 1;
216
216
  }
217
217
  if (!resolved.projectUrl) {
218
218
  console.error("No ORY_PROJECT_URL configured — cannot write permissions.");
219
- console.error(`Configure first: ${binName} configure --project-url <URL> --api-key <KEY>`);
219
+ console.error(`Configure first: ${binName} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
220
220
  return 1;
221
221
  }
222
222
  if (catalog.length === 0) {
@@ -235,7 +235,7 @@ async function runPermissionsBootstrap(binName, harness, args) {
235
235
  console.error("No user identity resolved — refusing to write permissions for an unknown subject.");
236
236
  console.error("");
237
237
  console.error("Either:");
238
- console.error(" - run the harness once with ORY_USER_LOGIN=1 so a user token is cached, or");
238
+ console.error(" - run the harness once with ORY_USER_LOGIN=true so a user token is cached, or");
239
239
  console.error(" - set ORY_USER_SUBJECT_ID=<id> to target a known subject.");
240
240
  return 1;
241
241
  }
package/dist/setup.js CHANGED
@@ -374,7 +374,9 @@ Environment variables:
374
374
  ORY_PROJECT_URL Your Ory project URL (required at runtime)
375
375
  ORY_AGENT_API_KEY Agent API key / OAuth2 bearer (preferred name;
376
376
  ORY_API_KEY is honored as a deprecated alias)
377
- ORY_USER_LOGIN Set to "1" to enable the interactive user login
377
+ ORY_OAUTH2_CLIENT_ID Public OAuth2 client id for the user PKCE flow
378
+ (required when ORY_USER_LOGIN=true)
379
+ ORY_USER_LOGIN Set to "true" to enable the interactive user login
378
380
  ORY_PERMISSION_MODE "observe" (default) or "enforce" — what to do on deny
379
381
  ORY_AGENT_DEBUG Set to "true" for debug logging
380
382
  ORY_AGENT_LOG_FILE Path to write debug logs
@@ -392,9 +394,12 @@ function printNextSteps(harnessName, uninstallCmd) {
392
394
  console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
393
395
  console.log("");
394
396
  console.log(" 2. Turn on the interactive user login (opt-in):");
395
- console.log(" export ORY_USER_LOGIN=1");
396
- console.log(` Without this, ${harnessName} runs without a human Ory identity attached`);
397
+ console.log(" export ORY_USER_LOGIN=true");
398
+ console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
399
+ console.log(` Without these, ${harnessName} runs without a human Ory identity attached`);
397
400
  console.log(" to the session and permission checks fall back to a session:<id> subject.");
401
+ console.log(" The OAuth2 client id is the public client (no secret) you registered in");
402
+ console.log(" your Ory project with the four loopback redirect URIs.");
398
403
  console.log("");
399
404
  console.log(" 3. Optionally enable debug logging:");
400
405
  console.log(" export ORY_AGENT_DEBUG=true");
@@ -57,7 +57,7 @@ function printUserIdentitySection() {
57
57
  const tokens = (0, auth_store_js_1.loadTokens)();
58
58
  console.log("");
59
59
  console.log("User identity (interactive PKCE login):");
60
- console.log(` Login: ${loginOn ? "on (ORY_USER_LOGIN)" : "off (set ORY_USER_LOGIN=1 to enable)"}`);
60
+ console.log(` Login: ${loginOn ? "on (ORY_USER_LOGIN)" : "off (set ORY_USER_LOGIN=true to enable)"}`);
61
61
  if (!tokens) {
62
62
  console.log(" Token cache: empty");
63
63
  if (loginOn) {
@@ -209,7 +209,7 @@ async function printPermissionsSection(binName, harness) {
209
209
  }
210
210
  const hasUser = !!process.env.ORY_USER_SUBJECT_ID || isUserTokenUsable();
211
211
  if (!hasUser) {
212
- console.log(` Coverage: n/a (no cached user identity — run with ORY_USER_LOGIN=1 once,`);
212
+ console.log(` Coverage: n/a (no cached user identity — run with ORY_USER_LOGIN=true once,`);
213
213
  console.log(` or set ORY_USER_SUBJECT_ID to probe a known subject)`);
214
214
  return;
215
215
  }
@@ -18,7 +18,8 @@
18
18
  * the audit trail is complete regardless of outcome.
19
19
  *
20
20
  * The whole flow is a no-op (mode `disabled`) unless the
21
- * `ORY_USER_LOGIN` env var is set to `1`/`true`.
21
+ * `ORY_USER_LOGIN` env var is set to `true` (legacy values `1`, `yes`,
22
+ * `on` are still accepted for back-compat).
22
23
  *
23
24
  * This authenticates the *user* (the human at the keyboard). The
24
25
  * separate agent identity (the AI process making the calls) is resolved
@@ -19,7 +19,8 @@
19
19
  * the audit trail is complete regardless of outcome.
20
20
  *
21
21
  * The whole flow is a no-op (mode `disabled`) unless the
22
- * `ORY_USER_LOGIN` env var is set to `1`/`true`.
22
+ * `ORY_USER_LOGIN` env var is set to `true` (legacy values `1`, `yes`,
23
+ * `on` are still accepted for back-compat).
23
24
  *
24
25
  * This authenticates the *user* (the human at the keyboard). The
25
26
  * separate agent identity (the AI process making the calls) is resolved
@@ -39,7 +40,10 @@ function isUserLoginEnabled() {
39
40
  return !!v && ENABLED_VALUES.has(v);
40
41
  }
41
42
  function clientId() {
42
- return process.env.ORY_OAUTH2_CLIENT_ID;
43
+ // resolveConfig already merges env (ORY_OAUTH2_CLIENT_ID) with the
44
+ // persisted value, env winning. Centralizing here keeps the user gate,
45
+ // status CLI, and configure command in lockstep on resolution order.
46
+ return (0, config_js_1.resolveConfig)().oauth2ClientId;
43
47
  }
44
48
  function recordAuthSpan(client, decision) {
45
49
  const status = decision.mode === "ok" || decision.mode === "refreshed" || decision.mode === "env_token"
@@ -211,7 +215,9 @@ async function runUserLogin(client, options) {
211
215
  mode: "skipped",
212
216
  reason: "ORY_OAUTH2_CLIENT_ID is not set; cannot start browser login (run `npx " +
213
217
  options.binName +
214
- " configure` and register an OAuth2 client with the loopback redirect URIs)",
218
+ " configure --oauth2-client-id <id>` after registering a public OAuth2 client " +
219
+ "in your Ory project with all four loopback redirect URIs: " +
220
+ "http://127.0.0.1:47823..47826/callback)",
215
221
  };
216
222
  }
217
223
  // No TTY check here on purpose: PKCE only needs a browser that can reach
@@ -221,7 +227,7 @@ async function runUserLogin(client, options) {
221
227
  // complete sign-in by pasting the URL into any browser that can reach
222
228
  // 127.0.0.1. Truly unattended runs (CI=true) are short-circuited by
223
229
  // `pkceLogin` itself via `detectHeadless`, and operators can bypass
224
- // login entirely with `ORY_USER_SESSION_TOKEN` or `ORY_USER_LOGIN=0`.
230
+ // login entirely with `ORY_USER_SESSION_TOKEN` or `ORY_USER_LOGIN=false`.
225
231
  // PKCE-flight lock so concurrent processes share a single browser flow.
226
232
  const lock = (0, auth_store_js_1.tryAcquirePkceFlightLock)();
227
233
  if (!lock) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ory/argus",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Ory Argus: the core API for building authentication, authorization, and audit into AI agent harness plugins, extensions, and custom integrations",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ory.com",