okengine 0.3.6 → 0.5.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/AGENTS.md +2 -0
- package/package.json +14 -12
- package/site/content/docs/ai/index.mdx +24 -0
- package/site/content/docs/ai/llms-txt.mdx +3 -0
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +5 -3
- package/site/content/docs/console/gates.mdx +46 -8
- package/site/content/docs/console/index.mdx +54 -0
- package/site/content/docs/console/meta.json +1 -0
- package/site/content/docs/elements/ai.mdx +2 -0
- package/site/content/docs/elements/channel.mdx +2 -0
- package/site/content/docs/elements/clock.mdx +1 -4
- package/site/content/docs/elements/flow.mdx +4 -10
- package/site/content/docs/elements/gate.mdx +189 -48
- package/site/content/docs/elements/index.mdx +45 -0
- package/site/content/docs/elements/meta.json +1 -1
- package/site/content/docs/elements/signal.mdx +1 -5
- package/site/content/docs/elements/store.mdx +27 -6
- package/site/content/docs/elements/vault.mdx +10 -11
- package/site/content/docs/get-started/basic-usage.mdx +79 -43
- package/site/content/docs/get-started/index.mdx +33 -0
- package/site/content/docs/get-started/installation.mdx +95 -43
- package/site/content/docs/get-started/introduction.mdx +128 -75
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/why.mdx +141 -0
- package/site/content/docs/index.mdx +9 -31
- package/site/content/docs/plugins/anonymous.mdx +95 -0
- package/site/content/docs/plugins/compression.mdx +2 -2
- package/site/content/docs/plugins/cors.mdx +2 -2
- package/site/content/docs/plugins/csrf.mdx +2 -2
- package/site/content/docs/plugins/email-otp.mdx +111 -0
- package/site/content/docs/plugins/{security-headers.mdx → headers.mdx} +2 -2
- package/site/content/docs/plugins/index.mdx +69 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
- package/site/content/docs/plugins/magic-link.mdx +112 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +8 -8
- package/site/content/docs/plugins/meta.json +10 -1
- package/site/content/docs/plugins/passkey.mdx +128 -0
- package/site/content/docs/plugins/phone-number.mdx +111 -0
- package/site/content/docs/plugins/two-factor.mdx +116 -0
- package/site/content/docs/plugins/username.mdx +117 -0
- package/site/content/docs/reference/client.mdx +331 -0
- package/site/content/docs/reference/configuration.mdx +1 -1
- package/site/content/docs/reference/environment-variables.mdx +5 -3
- package/site/content/docs/reference/fx.mdx +36 -8
- package/site/content/docs/reference/index.mdx +45 -0
- package/site/content/docs/reference/meta.json +11 -1
- package/site/content/docs/reference/plugins.mdx +25 -14
- package/src/auth/auth.test.ts +20 -2
- package/src/auth/bindings.ts +439 -0
- package/src/auth/breach-check.ts +112 -0
- package/src/auth/config.ts +288 -0
- package/src/auth/cookies.ts +123 -0
- package/src/auth/gate-auth.test.ts +379 -0
- package/src/auth/identity.ts +190 -0
- package/src/auth/index.ts +117 -1
- package/src/auth/method-context.ts +33 -0
- package/src/auth/operator.ts +27 -1
- package/src/auth/password-policy.test.ts +126 -0
- package/src/auth/password-policy.ts +77 -0
- package/src/auth/plugin.ts +62 -4
- package/src/auth/rate.ts +45 -0
- package/src/auth/schema.ts +260 -0
- package/src/auth/secondary-storage.ts +37 -0
- package/src/auth/sessions.ts +58 -1
- package/src/auth/tables.ts +4 -0
- package/src/auth/verification.ts +78 -0
- package/src/cli/competitor-mention-removal.test.ts +117 -0
- package/src/cli/dev.test.ts +3 -3
- package/src/cli/dev.ts +20 -0
- package/src/cli/meilisearch-local.test.ts +69 -0
- package/src/cli/meilisearch-local.ts +188 -0
- package/src/cli/schema.ts +95 -23
- package/src/client/auth.ts +120 -0
- package/src/client-react/index.ts +93 -0
- package/src/compiler/aot.test.ts +2 -1
- package/src/compiler/extract.ts +19 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
- package/src/compiler/response.ts +16 -2
- package/src/console/server/app.ts +10 -6
- package/src/console/server/auth-rate.test.ts +3 -3
- package/src/console/server/bind.ts +12 -1
- package/src/console/server/channels.test.ts +1 -1
- package/src/console/server/console-gates.ts +14 -0
- package/src/console/server/console.test.ts +6 -6
- package/src/console/server/flows-invoke.test.ts +2 -2
- package/src/console/server/flows.ts +2 -0
- package/src/console/server/gates.ts +8 -1
- package/src/console/server/operator-db.test.ts +4 -4
- package/src/console/server/operator-db.ts +22 -4
- package/src/console/server/security.gate.test.ts +3 -3
- package/src/console/server/store.test.ts +1 -1
- package/src/console/server/store.ts +11 -1
- package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
- package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
- package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-DCDd4WAC.js → panel-channels-9T3ybqRu.js} +1 -1
- package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
- package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
- package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
- package/src/console/ui/dist/assets/{panel-gates-B5eTE8XH.js → panel-gates-BQGYXvjT.js} +1 -1
- package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
- package/src/console/ui/dist/assets/{panel-plugins-Cj7DK1er.js → panel-plugins-D0PsmVw2.js} +1 -1
- package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
- package/src/console/ui/dist/assets/{panel-signals-whmDXIg3.js → panel-signals-Bbg4ewpP.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-CEMHLvaw.js → panel-store-CPCbsDRa.js} +1 -1
- package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
- package/src/console/ui/dist/assets/{panel-vault-C9wjbki8.js → panel-vault-D1_MvOmo.js} +1 -1
- package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/gates/fixture.ts +4 -0
- package/src/console/ui/gates/types.ts +2 -0
- package/src/console/ui/shell/client.ts +1 -0
- package/src/docker/compose.ts +5 -0
- package/src/docker/docker.test.ts +41 -0
- package/src/docker/recipes/index.ts +10 -2
- package/src/docker/recipes/meilisearch.ts +31 -0
- package/src/drivers/conformance.test.ts +16 -1
- package/src/drivers/conformance.ts +40 -3
- package/src/drivers/index.ts +14 -2
- package/src/drivers/libsql.ts +4 -4
- package/src/drivers/meilisearch.integration.test.ts +77 -0
- package/src/drivers/meilisearch.test.ts +181 -0
- package/src/drivers/meilisearch.ts +208 -0
- package/src/drivers/memory.ts +4 -4
- package/src/drivers/pgvector.ts +6 -6
- package/src/drivers/types.ts +93 -12
- package/src/drivers/vault-driver-removal.test.ts +6 -0
- package/src/drivers/vault-types.ts +4 -4
- package/src/elements/ai/runtime.ts +6 -0
- package/src/elements/ai.test.ts +22 -0
- package/src/elements/gate/boot.ts +136 -0
- package/src/elements/gate/config.ts +69 -0
- package/src/elements/gate/declare.ts +51 -1
- package/src/elements/gate/runtime.ts +3 -1
- package/src/elements/gate.test.ts +77 -0
- package/src/elements/gate.ts +20 -1
- package/src/elements/index.ts +8 -0
- package/src/elements/store/index-boot.test.ts +49 -7
- package/src/elements/store/runtime.ts +50 -15
- package/src/elements/store.ts +2 -0
- package/src/elements/vault.test.ts +27 -4
- package/src/elements/vault.ts +1 -1
- package/src/index.ts +15 -0
- package/src/kernel/app.ts +253 -32
- package/src/kernel/boot-bind/store.test.ts +9 -0
- package/src/kernel/boot-bind/store.ts +30 -2
- package/src/kernel/boot.test.ts +40 -3
- package/src/kernel/boot.ts +8 -0
- package/src/kernel/call.test.ts +46 -2
- package/src/kernel/concurrency.test.ts +58 -0
- package/src/kernel/concurrency.ts +48 -0
- package/src/kernel/edge.test.ts +3 -3
- package/src/kernel/flow.test.ts +2 -2
- package/src/kernel/fx.test.ts +12 -2
- package/src/kernel/fx.ts +97 -5
- package/src/kernel/hooks.test.ts +4 -4
- package/src/kernel/index.ts +22 -1
- package/src/kernel/pipeline.test.ts +12 -8
- package/src/kernel/pipeline.ts +23 -4
- package/src/kernel/plugin/decorate.test.ts +3 -3
- package/src/kernel/plugin/scoping.test.ts +3 -3
- package/src/kernel/plugin-elements.test.ts +51 -0
- package/src/kernel/plugin-needs.test.ts +83 -0
- package/src/kernel/plugin-needs.ts +129 -0
- package/src/kernel/plugin.ts +101 -0
- package/src/kernel/redacted.ts +74 -0
- package/src/kernel/registry-isolation.test.ts +5 -5
- package/src/kernel/registry.ts +102 -3
- package/src/kernel/router.ts +3 -3
- package/src/manifest/types.ts +2 -0
- package/src/plugins/anonymous.ts +58 -0
- package/src/plugins/auth/shared.ts +121 -0
- package/src/plugins/auth-methods.test.ts +176 -0
- package/src/plugins/compression.test.ts +5 -5
- package/src/plugins/config-source.test.ts +1 -1
- package/src/plugins/cors.test.ts +16 -10
- package/src/plugins/csrf.test.ts +1 -1
- package/src/plugins/email-otp.ts +161 -0
- package/src/plugins/index.ts +31 -0
- package/src/plugins/ip-allowlist.test.ts +19 -9
- package/src/plugins/magic-link.ts +163 -0
- package/src/plugins/maintenance-mode.test.ts +9 -5
- package/src/plugins/passkey.ts +216 -0
- package/src/plugins/phone-number.ts +149 -0
- package/src/plugins/security-headers.test.ts +14 -14
- package/src/plugins/two-factor.ts +249 -0
- package/src/plugins/username.ts +148 -0
- package/src/runs/runs.test.ts +6 -2
- package/src/runtime/primitives.ts +37 -4
- package/src/runtime/serve.test.ts +3 -2
- package/src/runtime/types.ts +25 -2
- package/src/test/create-test-app.test.ts +1 -1
- package/src/test/create-test-app.ts +4 -1
- package/src/test/provisions.integration.test.ts +2 -2
- package/site/content/docs/get-started/comparison.mdx +0 -65
- package/src/console/ui/dist/assets/index-CrKMmO__.js +0 -10
- package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
- package/src/console/ui/dist/assets/panel-clock-DjGGFPzr.js +0 -1
- package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
- package/src/console/ui/dist/assets/panel-overview-BsFvDdts.js +0 -1
- package/src/console/ui/dist/assets/panel-runs-C0gmnoYL.js +0 -1
- package/src/console/ui/dist/assets/panel-traces-BDiAuVSK.js +0 -1
- package/src/drivers/vault-infisical.ts +0 -57
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Email OTP"
|
|
3
|
+
description: "Official plugin — six-digit email one-time codes to sign in under /auth."
|
|
4
|
+
icon: "MailCheck"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`emailOtp()` stores a hashed 6-digit code (default 10 minutes, 5 attempts). Verify creates the
|
|
9
|
+
user on first success and returns hybrid session tokens.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Enable `gate.auth`, then `.plug(emailOtp())`. Never log raw OTPs. Channel email delivery is not
|
|
13
|
+
wired yet — use `exposeDevOtp` locally.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Plug it
|
|
22
|
+
|
|
23
|
+
```typescript title="src/app.ts"
|
|
24
|
+
import { oke } from "okengine";
|
|
25
|
+
import { emailOtp } from "okengine/plugins";
|
|
26
|
+
|
|
27
|
+
export const app = oke({
|
|
28
|
+
name: "shop",
|
|
29
|
+
env: "local",
|
|
30
|
+
gate: { auth: {} },
|
|
31
|
+
}).plug(emailOtp({ exposeDevOtp: true }));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</Step>
|
|
35
|
+
|
|
36
|
+
<Step>
|
|
37
|
+
### Request a code
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const { data } = await api.auth.requestEmailOtp({ email: "ali@example.com" });
|
|
41
|
+
// data.devOtp when exposeDevOtp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`POST /auth/email-otp/request` — prior active codes for that email are invalidated.
|
|
45
|
+
|
|
46
|
+
</Step>
|
|
47
|
+
|
|
48
|
+
<Step>
|
|
49
|
+
### Verify
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const { data } = await api.auth.verifyEmailOtp({
|
|
53
|
+
email: "ali@example.com",
|
|
54
|
+
otp: data!.devOtp!,
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`POST /auth/email-otp/verify`. Wrong code increments attempts; after 5 →
|
|
59
|
+
`invalid_credentials`.
|
|
60
|
+
|
|
61
|
+
</Step>
|
|
62
|
+
|
|
63
|
+
</Steps>
|
|
64
|
+
|
|
65
|
+
## Options
|
|
66
|
+
|
|
67
|
+
| Option | Type | Default | Meaning |
|
|
68
|
+
| --------------- | ------------------- | -------- | --------------------------------------- |
|
|
69
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
70
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
71
|
+
| `ttlMs` | `number` | 10m | Challenge lifetime |
|
|
72
|
+
| `exposeDevOtp` | `boolean` | `false` | Include raw OTP in the request response |
|
|
73
|
+
| `identities` | `IdentityStore` | new | Email → user map |
|
|
74
|
+
| `verifications` | `VerificationStore` | new | Challenge store |
|
|
75
|
+
|
|
76
|
+
## Surfaces
|
|
77
|
+
|
|
78
|
+
| Flow | Path | Gate |
|
|
79
|
+
| ---------------------- | ------------------------------ | ------------------------ |
|
|
80
|
+
| `auth.requestEmailOtp` | `POST /auth/email-otp/request` | `gate.public` + otp rate |
|
|
81
|
+
| `auth.verifyEmailOtp` | `POST /auth/email-otp/verify` | `gate.public` + otp rate |
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
<Accordions>
|
|
86
|
+
<Accordion title="No email with the code">
|
|
87
|
+
|
|
88
|
+
v1 does not send mail. Use `exposeDevOtp` in local/test, or send the code via your own Channel
|
|
89
|
+
Flow.
|
|
90
|
+
|
|
91
|
+
</Accordion>
|
|
92
|
+
<Accordion title="AuthFailed after a few tries">
|
|
93
|
+
|
|
94
|
+
Five failed attempts consume the challenge. Request a new OTP.
|
|
95
|
+
|
|
96
|
+
</Accordion>
|
|
97
|
+
</Accordions>
|
|
98
|
+
|
|
99
|
+
## Learn more
|
|
100
|
+
|
|
101
|
+
- [Magic link](/docs/plugins/magic-link) — link instead of a code
|
|
102
|
+
- [Phone number](/docs/plugins/phone-number) — SMS OTP (E.164)
|
|
103
|
+
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
104
|
+
|
|
105
|
+
## Next
|
|
106
|
+
|
|
107
|
+
<Cards>
|
|
108
|
+
<Card title="Phone number" description="E.164 SMS OTP." href="/docs/plugins/phone-number" />
|
|
109
|
+
<Card title="Magic link" description="Email link sign-in." href="/docs/plugins/magic-link" />
|
|
110
|
+
<Card title="Two-factor" description="TOTP step-up." href="/docs/plugins/two-factor" />
|
|
111
|
+
</Cards>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Headers"
|
|
3
3
|
description: "Official plugin — the complete secure-headers set on every HTTP response, failures included. Full helmet.js parity with API-first defaults, a CSP builder with report-only mode, and live DB-driven config."
|
|
4
4
|
icon: "ShieldCheck"
|
|
5
5
|
source: "src/plugins/security-headers.ts"
|
|
@@ -51,7 +51,7 @@ Every [helmet.js](https://helmet.js.org/) middleware maps to an option here —
|
|
|
51
51
|
| `xPoweredBy` | `poweredBy` | Yes — removed; a string sets a decoy value |
|
|
52
52
|
| `xXssProtection` | `xssProtection` | Yes — `0` (disables the legacy buggy auditor) |
|
|
53
53
|
|
|
54
|
-
Beyond parity: headers land on **failures too** (
|
|
54
|
+
Beyond parity: headers land on **failures too** (middleware that only wraps happy paths skips error responses), app-set values win by default, and every option can be driven live from the database (below).
|
|
55
55
|
|
|
56
56
|
## Options
|
|
57
57
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Plugins"
|
|
3
|
+
description: "Official okengine/plugins extensions — authentication, security, operations, and performance."
|
|
4
|
+
icon: "Puzzle"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
First-party plugins you `.plug()` onto an app. Each page is one export from `okengine/plugins`.
|
|
9
|
+
|
|
10
|
+
## Authentication
|
|
11
|
+
|
|
12
|
+
<Cards>
|
|
13
|
+
<Card title="Username" description="Username + password." href="/docs/plugins/username" />
|
|
14
|
+
<Card
|
|
15
|
+
title="Anonymous"
|
|
16
|
+
description="Guest session, no password."
|
|
17
|
+
href="/docs/plugins/anonymous"
|
|
18
|
+
/>
|
|
19
|
+
<Card title="Magic link" description="One-time email link." href="/docs/plugins/magic-link" />
|
|
20
|
+
<Card title="Email OTP" description="Six-digit email codes." href="/docs/plugins/email-otp" />
|
|
21
|
+
<Card title="Phone number" description="E.164 SMS OTP." href="/docs/plugins/phone-number" />
|
|
22
|
+
<Card title="Two-factor" description="TOTP enable / verify." href="/docs/plugins/two-factor" />
|
|
23
|
+
<Card title="Passkey" description="WebAuthn-shaped passkeys." href="/docs/plugins/passkey" />
|
|
24
|
+
</Cards>
|
|
25
|
+
|
|
26
|
+
## Security
|
|
27
|
+
|
|
28
|
+
<Cards>
|
|
29
|
+
<Card
|
|
30
|
+
title="Headers"
|
|
31
|
+
description="Secure headers on every HTTP response."
|
|
32
|
+
href="/docs/plugins/headers"
|
|
33
|
+
/>
|
|
34
|
+
<Card
|
|
35
|
+
title="CORS"
|
|
36
|
+
description="Cross-origin rules; closed by default."
|
|
37
|
+
href="/docs/plugins/cors"
|
|
38
|
+
/>
|
|
39
|
+
<Card
|
|
40
|
+
title="CSRF"
|
|
41
|
+
description="Fetch-metadata forgery defense, no tokens."
|
|
42
|
+
href="/docs/plugins/csrf"
|
|
43
|
+
/>
|
|
44
|
+
<Card
|
|
45
|
+
title="IP Allowlist"
|
|
46
|
+
description="Allow/deny by client IP at the edge."
|
|
47
|
+
href="/docs/plugins/ip-allowlist"
|
|
48
|
+
/>
|
|
49
|
+
</Cards>
|
|
50
|
+
|
|
51
|
+
## Operations
|
|
52
|
+
|
|
53
|
+
<Cards>
|
|
54
|
+
<Card
|
|
55
|
+
title="Maintenance Mode"
|
|
56
|
+
description="Drain HTTP with 503 and Retry-After."
|
|
57
|
+
href="/docs/plugins/maintenance-mode"
|
|
58
|
+
/>
|
|
59
|
+
</Cards>
|
|
60
|
+
|
|
61
|
+
## Performance
|
|
62
|
+
|
|
63
|
+
<Cards>
|
|
64
|
+
<Card
|
|
65
|
+
title="Compression"
|
|
66
|
+
description="gzip responses when the client accepts it."
|
|
67
|
+
href="/docs/plugins/compression"
|
|
68
|
+
/>
|
|
69
|
+
</Cards>
|
|
@@ -46,8 +46,7 @@ A client whose IP is not on the list receives `403` with a typed denial:
|
|
|
46
46
|
<Callout type="error">
|
|
47
47
|
Standard reverse proxies **append** to `X-Forwarded-For` — left-side hops are attacker-controlled.
|
|
48
48
|
The plugin trusts the hop `trustedProxyDepth` from the **right** (default `1` = last hop). Set
|
|
49
|
-
this to your real proxy count — wrong depth bypasses the allowlist
|
|
50
|
-
drop-in).
|
|
49
|
+
this to your real proxy count — wrong depth bypasses the allowlist.
|
|
51
50
|
</Callout>
|
|
52
51
|
|
|
53
52
|
## Notes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Magic link"
|
|
3
|
+
description: "Official plugin — request and verify a one-time email link to sign in under /auth."
|
|
4
|
+
icon: "Link2"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`magicLink()` issues a hashed, single-use token (default 10 minutes). Verify exchanges it for a
|
|
9
|
+
hybrid session and creates the user on first success.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Enable `gate.auth`, then `.plug(magicLink())`. Tokens are hashed at rest — never log the raw link.
|
|
13
|
+
Channel email delivery is not wired yet; use `exposeDevToken` locally.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Plug it
|
|
22
|
+
|
|
23
|
+
```typescript title="src/app.ts"
|
|
24
|
+
import { oke } from "okengine";
|
|
25
|
+
import { magicLink } from "okengine/plugins";
|
|
26
|
+
|
|
27
|
+
export const app = oke({
|
|
28
|
+
name: "shop",
|
|
29
|
+
env: "local",
|
|
30
|
+
gate: { auth: {} },
|
|
31
|
+
}).plug(magicLink({ exposeDevToken: true }));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</Step>
|
|
35
|
+
|
|
36
|
+
<Step>
|
|
37
|
+
### Request a link
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const { data } = await api.auth.requestMagicLink({ email: "ali@example.com" });
|
|
41
|
+
// data.ok === true; data.devToken only when exposeDevToken
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`POST /auth/magic-link/request`.
|
|
45
|
+
|
|
46
|
+
</Step>
|
|
47
|
+
|
|
48
|
+
<Step>
|
|
49
|
+
### Verify
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const { data } = await api.auth.verifyMagicLink({ token: data!.devToken! });
|
|
53
|
+
// session tokens + userId
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`POST /auth/magic-link/verify`. Bad or reused tokens → `AuthFailed` /
|
|
57
|
+
`invalid_credentials`.
|
|
58
|
+
|
|
59
|
+
</Step>
|
|
60
|
+
|
|
61
|
+
</Steps>
|
|
62
|
+
|
|
63
|
+
## Options
|
|
64
|
+
|
|
65
|
+
| Option | Type | Default | Meaning |
|
|
66
|
+
| ---------------- | ------------------- | -------- | ----------------------------------------- |
|
|
67
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
68
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
69
|
+
| `ttlMs` | `number` | 10m | Challenge lifetime |
|
|
70
|
+
| `exposeDevToken` | `boolean` | `false` | Include raw token in the request response |
|
|
71
|
+
| `identities` | `IdentityStore` | new | Email → user map |
|
|
72
|
+
| `verifications` | `VerificationStore` | new | Challenge store |
|
|
73
|
+
|
|
74
|
+
## Surfaces
|
|
75
|
+
|
|
76
|
+
| Flow | Path | Gate |
|
|
77
|
+
| ----------------------- | ------------------------------- | ------------------------ |
|
|
78
|
+
| `auth.requestMagicLink` | `POST /auth/magic-link/request` | `gate.public` + otp rate |
|
|
79
|
+
| `auth.verifyMagicLink` | `POST /auth/magic-link/verify` | `gate.public` + otp rate |
|
|
80
|
+
|
|
81
|
+
**Consequence:** until Channel delivery ships, production apps must send the link themselves
|
|
82
|
+
(or keep `exposeDevToken` off and never log tokens).
|
|
83
|
+
|
|
84
|
+
## Troubleshooting
|
|
85
|
+
|
|
86
|
+
<Accordions>
|
|
87
|
+
<Accordion title="verify returns invalid_credentials">
|
|
88
|
+
|
|
89
|
+
Token expired (default 10m), already used, or mistyped. Request a new link.
|
|
90
|
+
|
|
91
|
+
</Accordion>
|
|
92
|
+
<Accordion title="No email arrived">
|
|
93
|
+
|
|
94
|
+
v1 does not send mail. Use `exposeDevToken` in local/test, or deliver `devToken` / your own
|
|
95
|
+
link via a Channel Flow you own.
|
|
96
|
+
|
|
97
|
+
</Accordion>
|
|
98
|
+
</Accordions>
|
|
99
|
+
|
|
100
|
+
## Learn more
|
|
101
|
+
|
|
102
|
+
- [Email OTP](/docs/plugins/email-otp) — numeric code instead of a link
|
|
103
|
+
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
104
|
+
- [Channel](/docs/elements/channel) — when you wire delivery yourself
|
|
105
|
+
|
|
106
|
+
## Next
|
|
107
|
+
|
|
108
|
+
<Cards>
|
|
109
|
+
<Card title="Email OTP" description="Six-digit email codes." href="/docs/plugins/email-otp" />
|
|
110
|
+
<Card title="Gate" description="Builtin auth and policies." href="/docs/elements/gate" />
|
|
111
|
+
<Card title="Username" description="Password sign-in." href="/docs/plugins/username" />
|
|
112
|
+
</Cards>
|
|
@@ -60,12 +60,12 @@ With `MAINTENANCE_MODE=1` in the environment, every HTTP flow answers:
|
|
|
60
60
|
|
|
61
61
|
## Notes
|
|
62
62
|
|
|
63
|
-
| Behavior | Detail
|
|
64
|
-
| ----------------- |
|
|
65
|
-
| Pipeline position | `onRequest` — flows never parse, authenticate, or execute
|
|
66
|
-
| Still shaped | The 503 flows through `onResponse`, so
|
|
67
|
-
| Non-HTTP triggers | No-op — clock flows and signal subscribers keep running
|
|
68
|
-
| Infra routes | `/_oke/*` bypass the pipeline entirely and stay up
|
|
63
|
+
| Behavior | Detail |
|
|
64
|
+
| ----------------- | -------------------------------------------------------------------------- |
|
|
65
|
+
| Pipeline position | `onRequest` — flows never parse, authenticate, or execute |
|
|
66
|
+
| Still shaped | The 503 flows through `onResponse`, so Headers and Compression apply to it |
|
|
67
|
+
| Non-HTTP triggers | No-op — clock flows and signal subscribers keep running |
|
|
68
|
+
| Infra routes | `/_oke/*` bypass the pipeline entirely and stay up |
|
|
69
69
|
|
|
70
70
|
## Runtime configuration
|
|
71
71
|
|
|
@@ -93,9 +93,9 @@ See [Plugins → Runtime configuration](/docs/reference/plugins#runtime-configur
|
|
|
93
93
|
href="/docs/plugins/ip-allowlist"
|
|
94
94
|
/>
|
|
95
95
|
<Card
|
|
96
|
-
title="
|
|
96
|
+
title="Headers"
|
|
97
97
|
description="The full secure-headers set on every response."
|
|
98
|
-
href="/docs/plugins/
|
|
98
|
+
href="/docs/plugins/headers"
|
|
99
99
|
/>
|
|
100
100
|
<Card title="Plugin API" description="Build your own plugin." href="/docs/reference/plugins" />
|
|
101
101
|
</Cards>
|
|
@@ -2,8 +2,17 @@
|
|
|
2
2
|
"title": "Plugins",
|
|
3
3
|
"icon": "Puzzle",
|
|
4
4
|
"pages": [
|
|
5
|
+
"index",
|
|
6
|
+
"---Authentication---",
|
|
7
|
+
"username",
|
|
8
|
+
"anonymous",
|
|
9
|
+
"magic-link",
|
|
10
|
+
"email-otp",
|
|
11
|
+
"phone-number",
|
|
12
|
+
"two-factor",
|
|
13
|
+
"passkey",
|
|
5
14
|
"---Security---",
|
|
6
|
-
"
|
|
15
|
+
"headers",
|
|
7
16
|
"cors",
|
|
8
17
|
"csrf",
|
|
9
18
|
"ip-allowlist",
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Passkey"
|
|
3
|
+
description: "Official plugin — simplified WebAuthn register and authenticate under /auth."
|
|
4
|
+
icon: "FingerprintPattern"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`passkey()` adds register and authenticate Flows for passkey-shaped credentials (`oke_passkeys`).
|
|
9
|
+
v1 is a simplified ceremony — options return a challenge; register/authenticate accept the
|
|
10
|
+
payload your client posts (not a full browser WebAuthn SDK).
|
|
11
|
+
|
|
12
|
+
<Callout title="The one rule">
|
|
13
|
+
Enable `gate.auth`, then `.plug(passkey())`. Registration needs a Bearer session; authenticate is
|
|
14
|
+
public. Wire `navigator.credentials` (or tests) yourself — the plugin stores and verifies the
|
|
15
|
+
posted fields.
|
|
16
|
+
</Callout>
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
|
|
22
|
+
<Step>
|
|
23
|
+
### Plug it
|
|
24
|
+
|
|
25
|
+
```typescript title="src/app.ts"
|
|
26
|
+
import { oke } from "okengine";
|
|
27
|
+
import { passkey } from "okengine/plugins";
|
|
28
|
+
|
|
29
|
+
export const app = oke({
|
|
30
|
+
name: "shop",
|
|
31
|
+
env: "local",
|
|
32
|
+
gate: { auth: {} },
|
|
33
|
+
}).plug(passkey());
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</Step>
|
|
37
|
+
|
|
38
|
+
<Step>
|
|
39
|
+
### Register (session required)
|
|
40
|
+
|
|
41
|
+
Wire Bearer on `createClient` (`auth.getToken` / `memorySession`) — calls take input only.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const opts = await api.auth.passkeyRegisterOptions({});
|
|
45
|
+
// opts.data: { challenge, rpId, userId }
|
|
46
|
+
|
|
47
|
+
await api.auth.passkeyRegister({
|
|
48
|
+
credentialId: "...",
|
|
49
|
+
publicKey: "...",
|
|
50
|
+
userId: opts.data!.userId,
|
|
51
|
+
challenge: opts.data!.challenge,
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Paths: `POST /auth/passkey/register/options`, `POST /auth/passkey/register`.
|
|
56
|
+
|
|
57
|
+
</Step>
|
|
58
|
+
|
|
59
|
+
<Step>
|
|
60
|
+
### Authenticate
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
await api.auth.passkeyAuthenticateOptions({});
|
|
64
|
+
const { data } = await api.auth.passkeyAuthenticate({
|
|
65
|
+
credentialId: "...",
|
|
66
|
+
userId: "...",
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Paths: `POST /auth/passkey/authenticate/options`, `POST /auth/passkey/authenticate`.
|
|
71
|
+
v1 issues a session when the stored credential matches `userId`.
|
|
72
|
+
|
|
73
|
+
</Step>
|
|
74
|
+
|
|
75
|
+
</Steps>
|
|
76
|
+
|
|
77
|
+
## Options
|
|
78
|
+
|
|
79
|
+
| Option | Type | Default | Meaning |
|
|
80
|
+
| ------------ | ------------------- | ------------- | -------------------------------- |
|
|
81
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
82
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
83
|
+
| `now` | `() => number` | `Date.now` | Injectable clock |
|
|
84
|
+
| `passkeys` | `PasskeyStore` | new | Credential → user mapping |
|
|
85
|
+
| `challenges` | `VerificationStore` | new | Registration / auth challenges |
|
|
86
|
+
| `rpId` | `string` | `"localhost"` | Relying party id in options |
|
|
87
|
+
|
|
88
|
+
## Surfaces
|
|
89
|
+
|
|
90
|
+
| Flow | Path | Gate |
|
|
91
|
+
| --------------------------------- | ----------------------------------------- | ------------------------ |
|
|
92
|
+
| `auth.passkeyRegisterOptions` | `POST /auth/passkey/register/options` | session + bearer |
|
|
93
|
+
| `auth.passkeyRegister` | `POST /auth/passkey/register` | session + bearer |
|
|
94
|
+
| `auth.passkeyAuthenticateOptions` | `POST /auth/passkey/authenticate/options` | `gate.public` + otp rate |
|
|
95
|
+
| `auth.passkeyAuthenticate` | `POST /auth/passkey/authenticate` | `gate.public` + otp rate |
|
|
96
|
+
|
|
97
|
+
**Consequence:** production apps should replace the simplified verify with a standards-compliant
|
|
98
|
+
WebAuthn verifier when you leave local/test.
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
<Accordions>
|
|
103
|
+
<Accordion title="register fails with unauthenticated">
|
|
104
|
+
|
|
105
|
+
Sign in with another method first. `userId` in the body must match the Bearer session.
|
|
106
|
+
|
|
107
|
+
</Accordion>
|
|
108
|
+
<Accordion title="authenticate returns invalid_credentials">
|
|
109
|
+
|
|
110
|
+
Unknown `credentialId`, or `userId` does not match the stored credential. Re-run registration
|
|
111
|
+
after a successful session.
|
|
112
|
+
|
|
113
|
+
</Accordion>
|
|
114
|
+
</Accordions>
|
|
115
|
+
|
|
116
|
+
## Learn more
|
|
117
|
+
|
|
118
|
+
- [Two-factor](/docs/plugins/two-factor) — TOTP step-up
|
|
119
|
+
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
120
|
+
- [Client](/docs/reference/client) — calling `/auth` from the browser
|
|
121
|
+
|
|
122
|
+
## Next
|
|
123
|
+
|
|
124
|
+
<Cards>
|
|
125
|
+
<Card title="Two-factor" description="TOTP enable / verify." href="/docs/plugins/two-factor" />
|
|
126
|
+
<Card title="Gate" description="Builtin auth and policies." href="/docs/elements/gate" />
|
|
127
|
+
<Card title="Anonymous" description="Guest sessions." href="/docs/plugins/anonymous" />
|
|
128
|
+
</Cards>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Phone number"
|
|
3
|
+
description: "Official plugin — E.164 phone OTP request and verify under /auth."
|
|
4
|
+
icon: "Smartphone"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`phoneNumber()` signs people in with a phone number in E.164 form (`+15551234567`). Codes are
|
|
9
|
+
6 digits, hashed, 10 minutes, 5 attempts — same security shape as email OTP.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Enable `gate.auth`, then `.plug(phoneNumber())`. Reject non-E.164 numbers. SMS Channel delivery is
|
|
13
|
+
not wired yet — use `exposeDevOtp` locally. Prefer stronger methods when phone is the only factor.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Plug it
|
|
22
|
+
|
|
23
|
+
```typescript title="src/app.ts"
|
|
24
|
+
import { oke } from "okengine";
|
|
25
|
+
import { phoneNumber } from "okengine/plugins";
|
|
26
|
+
|
|
27
|
+
export const app = oke({
|
|
28
|
+
name: "shop",
|
|
29
|
+
env: "local",
|
|
30
|
+
gate: { auth: {} },
|
|
31
|
+
}).plug(phoneNumber({ exposeDevOtp: true }));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</Step>
|
|
35
|
+
|
|
36
|
+
<Step>
|
|
37
|
+
### Request a code
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const { data, error } = await api.auth.requestPhoneOtp({ phone: "+15551234567" });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`POST /auth/phone/request`. Invalid E.164 → `AuthFailed` / `invalid_phone`.
|
|
44
|
+
|
|
45
|
+
</Step>
|
|
46
|
+
|
|
47
|
+
<Step>
|
|
48
|
+
### Verify
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const { data } = await api.auth.verifyPhoneOtp({
|
|
52
|
+
phone: "+15551234567",
|
|
53
|
+
otp: data!.devOtp!,
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`POST /auth/phone/verify` — first success creates a phone → `userId` mapping.
|
|
58
|
+
|
|
59
|
+
</Step>
|
|
60
|
+
|
|
61
|
+
</Steps>
|
|
62
|
+
|
|
63
|
+
## Options
|
|
64
|
+
|
|
65
|
+
| Option | Type | Default | Meaning |
|
|
66
|
+
| --------------- | ------------------- | -------- | --------------------------------------- |
|
|
67
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
68
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
69
|
+
| `ttlMs` | `number` | 10m | Challenge lifetime |
|
|
70
|
+
| `exposeDevOtp` | `boolean` | `false` | Include raw OTP in the request response |
|
|
71
|
+
| `phones` | `PhoneStore` | new | Phone → userId map |
|
|
72
|
+
| `verifications` | `VerificationStore` | new | Challenge store |
|
|
73
|
+
|
|
74
|
+
## Surfaces
|
|
75
|
+
|
|
76
|
+
| Flow | Path | Gate |
|
|
77
|
+
| ---------------------- | -------------------------- | ------------------------ |
|
|
78
|
+
| `auth.requestPhoneOtp` | `POST /auth/phone/request` | `gate.public` + otp rate |
|
|
79
|
+
| `auth.verifyPhoneOtp` | `POST /auth/phone/verify` | `gate.public` + otp rate |
|
|
80
|
+
|
|
81
|
+
E.164 pattern: `^\+[1-9]\d{7,14}$`.
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
<Accordions>
|
|
86
|
+
<Accordion title="invalid_phone">
|
|
87
|
+
|
|
88
|
+
Number must start with `+` and a non-zero country digit, then 7–14 more digits. No spaces or
|
|
89
|
+
dashes.
|
|
90
|
+
|
|
91
|
+
</Accordion>
|
|
92
|
+
<Accordion title="No SMS arrived">
|
|
93
|
+
|
|
94
|
+
v1 does not send SMS. Use `exposeDevOtp` in local/test, or deliver via your own Channel Flow.
|
|
95
|
+
|
|
96
|
+
</Accordion>
|
|
97
|
+
</Accordions>
|
|
98
|
+
|
|
99
|
+
## Learn more
|
|
100
|
+
|
|
101
|
+
- [Email OTP](/docs/plugins/email-otp) — email variant of the same OTP rules
|
|
102
|
+
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
103
|
+
- [Channel](/docs/elements/channel) — when you wire SMS yourself
|
|
104
|
+
|
|
105
|
+
## Next
|
|
106
|
+
|
|
107
|
+
<Cards>
|
|
108
|
+
<Card title="Email OTP" description="Six-digit email codes." href="/docs/plugins/email-otp" />
|
|
109
|
+
<Card title="Two-factor" description="TOTP step-up." href="/docs/plugins/two-factor" />
|
|
110
|
+
<Card title="Gate" description="Builtin auth and policies." href="/docs/elements/gate" />
|
|
111
|
+
</Cards>
|