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.
Files changed (205) hide show
  1. package/AGENTS.md +2 -0
  2. package/package.json +14 -12
  3. package/site/content/docs/ai/index.mdx +24 -0
  4. package/site/content/docs/ai/llms-txt.mdx +3 -0
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +5 -3
  7. package/site/content/docs/console/gates.mdx +46 -8
  8. package/site/content/docs/console/index.mdx +54 -0
  9. package/site/content/docs/console/meta.json +1 -0
  10. package/site/content/docs/elements/ai.mdx +2 -0
  11. package/site/content/docs/elements/channel.mdx +2 -0
  12. package/site/content/docs/elements/clock.mdx +1 -4
  13. package/site/content/docs/elements/flow.mdx +4 -10
  14. package/site/content/docs/elements/gate.mdx +189 -48
  15. package/site/content/docs/elements/index.mdx +45 -0
  16. package/site/content/docs/elements/meta.json +1 -1
  17. package/site/content/docs/elements/signal.mdx +1 -5
  18. package/site/content/docs/elements/store.mdx +27 -6
  19. package/site/content/docs/elements/vault.mdx +10 -11
  20. package/site/content/docs/get-started/basic-usage.mdx +79 -43
  21. package/site/content/docs/get-started/index.mdx +33 -0
  22. package/site/content/docs/get-started/installation.mdx +95 -43
  23. package/site/content/docs/get-started/introduction.mdx +128 -75
  24. package/site/content/docs/get-started/meta.json +1 -1
  25. package/site/content/docs/get-started/why.mdx +141 -0
  26. package/site/content/docs/index.mdx +9 -31
  27. package/site/content/docs/plugins/anonymous.mdx +95 -0
  28. package/site/content/docs/plugins/compression.mdx +2 -2
  29. package/site/content/docs/plugins/cors.mdx +2 -2
  30. package/site/content/docs/plugins/csrf.mdx +2 -2
  31. package/site/content/docs/plugins/email-otp.mdx +111 -0
  32. package/site/content/docs/plugins/{security-headers.mdx → headers.mdx} +2 -2
  33. package/site/content/docs/plugins/index.mdx +69 -0
  34. package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
  35. package/site/content/docs/plugins/magic-link.mdx +112 -0
  36. package/site/content/docs/plugins/maintenance-mode.mdx +8 -8
  37. package/site/content/docs/plugins/meta.json +10 -1
  38. package/site/content/docs/plugins/passkey.mdx +128 -0
  39. package/site/content/docs/plugins/phone-number.mdx +111 -0
  40. package/site/content/docs/plugins/two-factor.mdx +116 -0
  41. package/site/content/docs/plugins/username.mdx +117 -0
  42. package/site/content/docs/reference/client.mdx +331 -0
  43. package/site/content/docs/reference/configuration.mdx +1 -1
  44. package/site/content/docs/reference/environment-variables.mdx +5 -3
  45. package/site/content/docs/reference/fx.mdx +36 -8
  46. package/site/content/docs/reference/index.mdx +45 -0
  47. package/site/content/docs/reference/meta.json +11 -1
  48. package/site/content/docs/reference/plugins.mdx +25 -14
  49. package/src/auth/auth.test.ts +20 -2
  50. package/src/auth/bindings.ts +439 -0
  51. package/src/auth/breach-check.ts +112 -0
  52. package/src/auth/config.ts +288 -0
  53. package/src/auth/cookies.ts +123 -0
  54. package/src/auth/gate-auth.test.ts +379 -0
  55. package/src/auth/identity.ts +190 -0
  56. package/src/auth/index.ts +117 -1
  57. package/src/auth/method-context.ts +33 -0
  58. package/src/auth/operator.ts +27 -1
  59. package/src/auth/password-policy.test.ts +126 -0
  60. package/src/auth/password-policy.ts +77 -0
  61. package/src/auth/plugin.ts +62 -4
  62. package/src/auth/rate.ts +45 -0
  63. package/src/auth/schema.ts +260 -0
  64. package/src/auth/secondary-storage.ts +37 -0
  65. package/src/auth/sessions.ts +58 -1
  66. package/src/auth/tables.ts +4 -0
  67. package/src/auth/verification.ts +78 -0
  68. package/src/cli/competitor-mention-removal.test.ts +117 -0
  69. package/src/cli/dev.test.ts +3 -3
  70. package/src/cli/dev.ts +20 -0
  71. package/src/cli/meilisearch-local.test.ts +69 -0
  72. package/src/cli/meilisearch-local.ts +188 -0
  73. package/src/cli/schema.ts +95 -23
  74. package/src/client/auth.ts +120 -0
  75. package/src/client-react/index.ts +93 -0
  76. package/src/compiler/aot.test.ts +2 -1
  77. package/src/compiler/extract.ts +19 -0
  78. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
  79. package/src/compiler/response.ts +16 -2
  80. package/src/console/server/app.ts +10 -6
  81. package/src/console/server/auth-rate.test.ts +3 -3
  82. package/src/console/server/bind.ts +12 -1
  83. package/src/console/server/channels.test.ts +1 -1
  84. package/src/console/server/console-gates.ts +14 -0
  85. package/src/console/server/console.test.ts +6 -6
  86. package/src/console/server/flows-invoke.test.ts +2 -2
  87. package/src/console/server/flows.ts +2 -0
  88. package/src/console/server/gates.ts +8 -1
  89. package/src/console/server/operator-db.test.ts +4 -4
  90. package/src/console/server/operator-db.ts +22 -4
  91. package/src/console/server/security.gate.test.ts +3 -3
  92. package/src/console/server/store.test.ts +1 -1
  93. package/src/console/server/store.ts +11 -1
  94. package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
  95. package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
  96. package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
  97. package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
  98. package/src/console/ui/dist/assets/{panel-channels-DCDd4WAC.js → panel-channels-9T3ybqRu.js} +1 -1
  99. package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
  100. package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
  101. package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
  102. package/src/console/ui/dist/assets/{panel-gates-B5eTE8XH.js → panel-gates-BQGYXvjT.js} +1 -1
  103. package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
  104. package/src/console/ui/dist/assets/{panel-plugins-Cj7DK1er.js → panel-plugins-D0PsmVw2.js} +1 -1
  105. package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
  106. package/src/console/ui/dist/assets/{panel-signals-whmDXIg3.js → panel-signals-Bbg4ewpP.js} +1 -1
  107. package/src/console/ui/dist/assets/{panel-store-CEMHLvaw.js → panel-store-CPCbsDRa.js} +1 -1
  108. package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
  109. package/src/console/ui/dist/assets/{panel-vault-C9wjbki8.js → panel-vault-D1_MvOmo.js} +1 -1
  110. package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
  111. package/src/console/ui/dist/index.html +2 -2
  112. package/src/console/ui/gates/fixture.ts +4 -0
  113. package/src/console/ui/gates/types.ts +2 -0
  114. package/src/console/ui/shell/client.ts +1 -0
  115. package/src/docker/compose.ts +5 -0
  116. package/src/docker/docker.test.ts +41 -0
  117. package/src/docker/recipes/index.ts +10 -2
  118. package/src/docker/recipes/meilisearch.ts +31 -0
  119. package/src/drivers/conformance.test.ts +16 -1
  120. package/src/drivers/conformance.ts +40 -3
  121. package/src/drivers/index.ts +14 -2
  122. package/src/drivers/libsql.ts +4 -4
  123. package/src/drivers/meilisearch.integration.test.ts +77 -0
  124. package/src/drivers/meilisearch.test.ts +181 -0
  125. package/src/drivers/meilisearch.ts +208 -0
  126. package/src/drivers/memory.ts +4 -4
  127. package/src/drivers/pgvector.ts +6 -6
  128. package/src/drivers/types.ts +93 -12
  129. package/src/drivers/vault-driver-removal.test.ts +6 -0
  130. package/src/drivers/vault-types.ts +4 -4
  131. package/src/elements/ai/runtime.ts +6 -0
  132. package/src/elements/ai.test.ts +22 -0
  133. package/src/elements/gate/boot.ts +136 -0
  134. package/src/elements/gate/config.ts +69 -0
  135. package/src/elements/gate/declare.ts +51 -1
  136. package/src/elements/gate/runtime.ts +3 -1
  137. package/src/elements/gate.test.ts +77 -0
  138. package/src/elements/gate.ts +20 -1
  139. package/src/elements/index.ts +8 -0
  140. package/src/elements/store/index-boot.test.ts +49 -7
  141. package/src/elements/store/runtime.ts +50 -15
  142. package/src/elements/store.ts +2 -0
  143. package/src/elements/vault.test.ts +27 -4
  144. package/src/elements/vault.ts +1 -1
  145. package/src/index.ts +15 -0
  146. package/src/kernel/app.ts +253 -32
  147. package/src/kernel/boot-bind/store.test.ts +9 -0
  148. package/src/kernel/boot-bind/store.ts +30 -2
  149. package/src/kernel/boot.test.ts +40 -3
  150. package/src/kernel/boot.ts +8 -0
  151. package/src/kernel/call.test.ts +46 -2
  152. package/src/kernel/concurrency.test.ts +58 -0
  153. package/src/kernel/concurrency.ts +48 -0
  154. package/src/kernel/edge.test.ts +3 -3
  155. package/src/kernel/flow.test.ts +2 -2
  156. package/src/kernel/fx.test.ts +12 -2
  157. package/src/kernel/fx.ts +97 -5
  158. package/src/kernel/hooks.test.ts +4 -4
  159. package/src/kernel/index.ts +22 -1
  160. package/src/kernel/pipeline.test.ts +12 -8
  161. package/src/kernel/pipeline.ts +23 -4
  162. package/src/kernel/plugin/decorate.test.ts +3 -3
  163. package/src/kernel/plugin/scoping.test.ts +3 -3
  164. package/src/kernel/plugin-elements.test.ts +51 -0
  165. package/src/kernel/plugin-needs.test.ts +83 -0
  166. package/src/kernel/plugin-needs.ts +129 -0
  167. package/src/kernel/plugin.ts +101 -0
  168. package/src/kernel/redacted.ts +74 -0
  169. package/src/kernel/registry-isolation.test.ts +5 -5
  170. package/src/kernel/registry.ts +102 -3
  171. package/src/kernel/router.ts +3 -3
  172. package/src/manifest/types.ts +2 -0
  173. package/src/plugins/anonymous.ts +58 -0
  174. package/src/plugins/auth/shared.ts +121 -0
  175. package/src/plugins/auth-methods.test.ts +176 -0
  176. package/src/plugins/compression.test.ts +5 -5
  177. package/src/plugins/config-source.test.ts +1 -1
  178. package/src/plugins/cors.test.ts +16 -10
  179. package/src/plugins/csrf.test.ts +1 -1
  180. package/src/plugins/email-otp.ts +161 -0
  181. package/src/plugins/index.ts +31 -0
  182. package/src/plugins/ip-allowlist.test.ts +19 -9
  183. package/src/plugins/magic-link.ts +163 -0
  184. package/src/plugins/maintenance-mode.test.ts +9 -5
  185. package/src/plugins/passkey.ts +216 -0
  186. package/src/plugins/phone-number.ts +149 -0
  187. package/src/plugins/security-headers.test.ts +14 -14
  188. package/src/plugins/two-factor.ts +249 -0
  189. package/src/plugins/username.ts +148 -0
  190. package/src/runs/runs.test.ts +6 -2
  191. package/src/runtime/primitives.ts +37 -4
  192. package/src/runtime/serve.test.ts +3 -2
  193. package/src/runtime/types.ts +25 -2
  194. package/src/test/create-test-app.test.ts +1 -1
  195. package/src/test/create-test-app.ts +4 -1
  196. package/src/test/provisions.integration.test.ts +2 -2
  197. package/site/content/docs/get-started/comparison.mdx +0 -65
  198. package/src/console/ui/dist/assets/index-CrKMmO__.js +0 -10
  199. package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
  200. package/src/console/ui/dist/assets/panel-clock-DjGGFPzr.js +0 -1
  201. package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
  202. package/src/console/ui/dist/assets/panel-overview-BsFvDdts.js +0 -1
  203. package/src/console/ui/dist/assets/panel-runs-C0gmnoYL.js +0 -1
  204. package/src/console/ui/dist/assets/panel-traces-BDiAuVSK.js +0 -1
  205. 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: "Security Headers"
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** (helmet middleware ordering bugs are a classic Express footgun), app-set values win by default, and every option can be driven live from the database (below).
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 (topology-dependent, not
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 Security 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 |
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="Security Headers"
96
+ title="Headers"
97
97
  description="The full secure-headers set on every response."
98
- href="/docs/plugins/security-headers"
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
- "security-headers",
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>