okengine 0.4.3 → 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/package.json +3 -1
- 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/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/gate.mdx +187 -48
- package/site/content/docs/elements/index.mdx +45 -0
- package/site/content/docs/elements/meta.json +1 -1
- package/site/content/docs/get-started/basic-usage.mdx +4 -3
- package/site/content/docs/get-started/index.mdx +33 -0
- package/site/content/docs/get-started/meta.json +1 -1
- 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} +1 -1
- package/site/content/docs/plugins/index.mdx +69 -0
- 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/fx.mdx +20 -5
- 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/dev.test.ts +3 -3
- 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/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/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/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/index.ts +11 -0
- package/src/kernel/app.ts +253 -32
- 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/edge.test.ts +3 -3
- package/src/kernel/flow.test.ts +2 -2
- package/src/kernel/fx.test.ts +1 -0
- package/src/kernel/fx.ts +60 -0
- package/src/kernel/hooks.test.ts +4 -4
- package/src/kernel/index.ts +12 -0
- 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/registry-isolation.test.ts +5 -5
- package/src/kernel/registry.ts +102 -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 +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Get Started"
|
|
3
|
+
description: "From the one law to a running app — introduction, why OKE, install, and first flows."
|
|
4
|
+
icon: "Rocket"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Learn the shape once, install on Bun, then write your first Flow from the standard starter.
|
|
9
|
+
|
|
10
|
+
## Pages
|
|
11
|
+
|
|
12
|
+
<Cards>
|
|
13
|
+
<Card
|
|
14
|
+
title="Introduction"
|
|
15
|
+
description="One law, eight elements, ten exports."
|
|
16
|
+
href="/docs/get-started/introduction"
|
|
17
|
+
/>
|
|
18
|
+
<Card
|
|
19
|
+
title="Why OKE"
|
|
20
|
+
description="Six seams every backend maintains by hand."
|
|
21
|
+
href="/docs/get-started/why"
|
|
22
|
+
/>
|
|
23
|
+
<Card
|
|
24
|
+
title="Installation"
|
|
25
|
+
description="Scaffold with create-oke and open the Console."
|
|
26
|
+
href="/docs/get-started/installation"
|
|
27
|
+
/>
|
|
28
|
+
<Card
|
|
29
|
+
title="Basic Usage"
|
|
30
|
+
description="Health Flow, typed client, Console proof."
|
|
31
|
+
href="/docs/get-started/basic-usage"
|
|
32
|
+
/>
|
|
33
|
+
</Cards>
|
|
@@ -23,41 +23,19 @@ on(orderPlaced, sendReceipt);
|
|
|
23
23
|
<Card
|
|
24
24
|
title="Get Started"
|
|
25
25
|
description="One law → install → first flows."
|
|
26
|
-
href="/docs/get-started
|
|
27
|
-
/>
|
|
28
|
-
<Card
|
|
29
|
-
title="Basic Usage"
|
|
30
|
-
description="Build from the standard starter."
|
|
31
|
-
href="/docs/get-started/basic-usage"
|
|
32
|
-
/>
|
|
33
|
-
<Card
|
|
34
|
-
title="Elements"
|
|
35
|
-
description="Flow → AI reference, one page each."
|
|
36
|
-
href="/docs/elements/flow"
|
|
37
|
-
/>
|
|
38
|
-
<Card
|
|
39
|
-
title="Console"
|
|
40
|
-
description="Seventeen Manifest-derived panels."
|
|
41
|
-
href="/docs/console/overview"
|
|
26
|
+
href="/docs/get-started"
|
|
42
27
|
/>
|
|
28
|
+
<Card title="Elements" description="Flow → AI reference, one page each." href="/docs/elements" />
|
|
43
29
|
<Card
|
|
44
30
|
title="Plugins"
|
|
45
|
-
description="Official extensions — security
|
|
46
|
-
href="/docs/plugins
|
|
47
|
-
/>
|
|
48
|
-
<Card
|
|
49
|
-
title="CLI Reference"
|
|
50
|
-
description="`oke` and `create-oke` everyday commands."
|
|
51
|
-
href="/docs/reference/cli"
|
|
52
|
-
/>
|
|
53
|
-
<Card
|
|
54
|
-
title="Security"
|
|
55
|
-
description="Console security posture — Host, Origin, MCP."
|
|
56
|
-
href="/docs/reference/security"
|
|
31
|
+
description="Official extensions — security, ops, performance."
|
|
32
|
+
href="/docs/plugins"
|
|
57
33
|
/>
|
|
34
|
+
<Card title="Console" description="Manifest-derived operator panels." href="/docs/console" />
|
|
58
35
|
<Card
|
|
59
|
-
title="
|
|
60
|
-
description="
|
|
61
|
-
href="/docs/
|
|
36
|
+
title="Reference"
|
|
37
|
+
description="Config, fx, env, errors, CLI, security."
|
|
38
|
+
href="/docs/reference"
|
|
62
39
|
/>
|
|
40
|
+
<Card title="AI Resources" description="MCP, skills, /llms.txt." href="/docs/ai" />
|
|
63
41
|
</Cards>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Anonymous"
|
|
3
|
+
description: "Official plugin — issue a user-plane session with a random id and no password."
|
|
4
|
+
icon: "UserRoundMinus"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`anonymous()` creates a throwaway principal: one public Flow returns hybrid session tokens for a
|
|
9
|
+
new random `userId`. Use it for guest carts or try-before-account flows.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Enable `gate.auth`, then `.plug(anonymous())`. Treat the session like any other Bearer principal —
|
|
13
|
+
gates still decide what it may do.
|
|
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 { anonymous } from "okengine/plugins";
|
|
26
|
+
|
|
27
|
+
export const app = oke({
|
|
28
|
+
name: "shop",
|
|
29
|
+
env: "local",
|
|
30
|
+
gate: { auth: {} },
|
|
31
|
+
}).plug(anonymous());
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</Step>
|
|
35
|
+
|
|
36
|
+
<Step>
|
|
37
|
+
### Sign in anonymously
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const { data } = await api.auth.signInAnonymous();
|
|
41
|
+
// data.userId is a fresh UUID; store tokens like any other session
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`POST /auth/sign-in/anonymous` — no body.
|
|
45
|
+
|
|
46
|
+
</Step>
|
|
47
|
+
|
|
48
|
+
<Step>
|
|
49
|
+
### Gate what guests can do
|
|
50
|
+
|
|
51
|
+
Attach real policies to guest-capable Flows (`gate.scope`, custom policies). Anonymous only
|
|
52
|
+
issues a session — it does not grant scopes.
|
|
53
|
+
|
|
54
|
+
</Step>
|
|
55
|
+
|
|
56
|
+
</Steps>
|
|
57
|
+
|
|
58
|
+
## Options
|
|
59
|
+
|
|
60
|
+
| Option | Type | Default | Meaning |
|
|
61
|
+
| ------------- | -------------- | ---------- | ----------------------------------------------------------- |
|
|
62
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth` when plugged after `oke()`) |
|
|
63
|
+
| `sessions` | `SessionStore` | active\* | Session store shared with Gate auth |
|
|
64
|
+
| `now` | `() => number` | `Date.now` | Injectable clock |
|
|
65
|
+
| `emailDomain` | `string` | — | Reserved; unused in v1 |
|
|
66
|
+
|
|
67
|
+
## Surfaces
|
|
68
|
+
|
|
69
|
+
| Flow | Path | Gate |
|
|
70
|
+
| ---------------------- | ------------------------------ | ---------------------------- |
|
|
71
|
+
| `auth.signInAnonymous` | `POST /auth/sign-in/anonymous` | `gate.public` + sign-in rate |
|
|
72
|
+
|
|
73
|
+
## Troubleshooting
|
|
74
|
+
|
|
75
|
+
<Accordions>
|
|
76
|
+
<Accordion title="plugin boot failed — needs "auth"">
|
|
77
|
+
|
|
78
|
+
Set `oke({ gate: { auth: { … } } })` before `.plug(anonymous())`.
|
|
79
|
+
|
|
80
|
+
</Accordion>
|
|
81
|
+
</Accordions>
|
|
82
|
+
|
|
83
|
+
## Learn more
|
|
84
|
+
|
|
85
|
+
- [Gate](/docs/elements/gate) — policies on the new principal
|
|
86
|
+
- [Username](/docs/plugins/username) — upgrade path to a real credential
|
|
87
|
+
- [Plugins](/docs/plugins) — all auth method plugins
|
|
88
|
+
|
|
89
|
+
## Next
|
|
90
|
+
|
|
91
|
+
<Cards>
|
|
92
|
+
<Card title="Username" description="Username + password." href="/docs/plugins/username" />
|
|
93
|
+
<Card title="Gate" description="Builtin auth and policies." href="/docs/elements/gate" />
|
|
94
|
+
<Card title="Magic link" description="Email link sign-in." href="/docs/plugins/magic-link" />
|
|
95
|
+
</Cards>
|
|
@@ -47,9 +47,9 @@ Thresholds and matchers can follow the database like every other official plugin
|
|
|
47
47
|
|
|
48
48
|
<Cards>
|
|
49
49
|
<Card
|
|
50
|
-
title="
|
|
50
|
+
title="Headers"
|
|
51
51
|
description="The full secure-headers set on every response."
|
|
52
|
-
href="/docs/plugins/
|
|
52
|
+
href="/docs/plugins/headers"
|
|
53
53
|
/>
|
|
54
54
|
<Card
|
|
55
55
|
title="IP Allowlist"
|
|
@@ -86,9 +86,9 @@ See [Plugins → Runtime configuration](/docs/reference/plugins#runtime-configur
|
|
|
86
86
|
href="/docs/plugins/csrf"
|
|
87
87
|
/>
|
|
88
88
|
<Card
|
|
89
|
-
title="
|
|
89
|
+
title="Headers"
|
|
90
90
|
description="The full secure-headers set on every response."
|
|
91
|
-
href="/docs/plugins/
|
|
91
|
+
href="/docs/plugins/headers"
|
|
92
92
|
/>
|
|
93
93
|
<Card
|
|
94
94
|
title="Plugin API"
|
|
@@ -89,8 +89,8 @@ See [Plugins → Runtime configuration](/docs/reference/plugins#runtime-configur
|
|
|
89
89
|
href="/docs/elements/gate"
|
|
90
90
|
/>
|
|
91
91
|
<Card
|
|
92
|
-
title="
|
|
92
|
+
title="Headers"
|
|
93
93
|
description="The full secure-headers set on every response."
|
|
94
|
-
href="/docs/plugins/
|
|
94
|
+
href="/docs/plugins/headers"
|
|
95
95
|
/>
|
|
96
96
|
</Cards>
|
|
@@ -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"
|
|
@@ -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>
|
|
@@ -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",
|