okengine 0.17.2 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/manifest.v1.schema.json +61 -2
- package/package.json +18 -4
- package/site/content/docs/elements/clock.mdx +1 -1
- package/site/content/docs/elements/flow.mdx +25 -1
- package/site/content/docs/elements/store.mdx +287 -341
- package/site/content/docs/elements/vault.mdx +5 -5
- package/site/content/docs/get-started/installation.mdx +1 -2
- package/site/content/docs/get-started/introduction.mdx +54 -110
- package/site/content/docs/get-started/meta.json +9 -1
- package/site/content/docs/get-started/testing.mdx +328 -0
- package/site/content/docs/get-started/why.mdx +94 -70
- package/site/content/docs/index.mdx +1 -1
- package/site/content/docs/plugins/apple.mdx +151 -0
- package/site/content/docs/plugins/discord.mdx +139 -0
- package/site/content/docs/plugins/facebook.mdx +134 -0
- package/site/content/docs/plugins/figma.mdx +138 -0
- package/site/content/docs/plugins/github.mdx +138 -0
- package/site/content/docs/plugins/google.mdx +153 -0
- package/site/content/docs/plugins/index.mdx +47 -1
- package/site/content/docs/plugins/meta.json +10 -0
- package/site/content/docs/plugins/microsoft.mdx +151 -0
- package/site/content/docs/plugins/oauth.mdx +188 -0
- package/site/content/docs/plugins/x.mdx +125 -0
- package/site/content/docs/reference/cli.md +3 -2
- package/site/content/docs/reference/client.mdx +58 -1
- package/site/content/docs/reference/configuration.mdx +2 -4
- package/site/content/docs/reference/fx.mdx +3 -1
- package/site/content/docs/reference/index.mdx +0 -5
- package/site/content/docs/reference/meta.json +2 -2
- package/site/content/docs/reference/okid.mdx +137 -0
- package/src/auth/bindings.ts +1 -1
- package/src/auth/config.ts +9 -0
- package/src/auth/identity-sql.ts +314 -0
- package/src/auth/identity.ts +140 -2
- package/src/auth/index.ts +17 -1
- package/src/auth/method-context.ts +3 -0
- package/src/auth/oauth-as/cimd.ts +132 -0
- package/src/auth/oauth-as/crypto.test.ts +101 -0
- package/src/auth/oauth-as/crypto.ts +393 -0
- package/src/auth/oauth-as/errors.ts +68 -0
- package/src/auth/oauth-as/http.test.ts +419 -0
- package/src/auth/oauth-as/http.ts +842 -0
- package/src/auth/oauth-as/stores.ts +61 -0
- package/src/auth/oauth-as/tables.ts +142 -0
- package/src/auth/tables.ts +0 -11
- package/src/bench/README.md +83 -0
- package/src/bench/REPORT.md +176 -0
- package/src/bench/g01-rls-stamp.bench.ts +194 -0
- package/src/bench/g02-clock-per-tenant.bench.ts +158 -0
- package/src/bench/g03-signal-once.bench.ts +157 -0
- package/src/bench/g03-signal-reconnect.bench.ts +254 -0
- package/src/bench/g03-signal-sse-memory.bench.ts +191 -0
- package/src/bench/g04-auth-vault-hotpath.bench.ts +170 -0
- package/src/bench/g05-sustained-full.bench.ts +265 -0
- package/src/bench/g06-mixed-load.bench.ts +260 -0
- package/src/bench/g07-vault-crypto.bench.ts +100 -0
- package/src/bench/g07-vault-rotate-under-read.bench.ts +285 -0
- package/src/bench/g08-conn-oversubscribe.bench.ts +194 -0
- package/src/bench/g08-store-kv-durable.bench.ts +133 -0
- package/src/bench/g08-store-sql.bench.ts +178 -0
- package/src/bench/g09-journal-sustained.bench.ts +203 -0
- package/src/bench/g10-observability-contention.bench.ts +246 -0
- package/src/bench/g11-cold-start-cycle.bench.ts +164 -0
- package/src/bench/g13-elements.bench.ts +427 -0
- package/src/bench/g14-graceful-shutdown.bench.ts +244 -0
- package/src/bench/g15-postgres-degradation.bench.ts +264 -0
- package/src/bench/g16-live-query-fanout.bench.ts +206 -0
- package/src/bench/lib/event-loop-lag.ts +26 -0
- package/src/bench/lib/infra.ts +60 -0
- package/src/bench/lib/report.ts +52 -0
- package/src/bench/lib/rss-sampler.ts +61 -0
- package/src/bench/lib/signal-pg.ts +88 -0
- package/src/bench/load-app.ts +337 -0
- package/src/bench/load-child.ts +108 -0
- package/src/bench/smoke.bench.ts +43 -0
- package/src/cli/competitor-mention-removal.test.ts +28 -0
- package/src/cli/doctor-fd.ts +117 -0
- package/src/cli/doctor.test.ts +192 -0
- package/src/cli/doctor.ts +129 -1
- package/src/client/create.ts +95 -1
- package/src/client/index.ts +9 -2
- package/src/client/transport.ts +11 -4
- package/src/client/use-live-query.ts +154 -0
- package/src/client-react/index.ts +15 -1
- package/src/client-react/live-resource.ts +246 -0
- package/src/client-react/use-live-query.test.ts +475 -0
- package/src/client-react/use-live-query.ts +530 -0
- package/src/compiler/extract.test.ts +518 -0
- package/src/compiler/extract.ts +386 -19
- package/src/console/server/invoke-user-flow.ts +2 -1
- package/src/console/ui-next/dist/assets/{access-page-DnWbnGzq.js → access-page-DY4N6nnk.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-BiZ4-6yQ.js → flows-page-CsPDMrVM.js} +1 -1
- package/src/console/ui-next/dist/assets/{index-C8NRK2R-.js → index-CcTDXHuz.js} +3 -3
- package/src/console/ui-next/dist/assets/{observability-page-CrB6vd1T.js → observability-page-CKR595wP.js} +1 -1
- package/src/console/ui-next/dist/assets/{store-page-CS5-aETQ.js → store-page-02xOiqIK.js} +3 -3
- package/src/console/ui-next/dist/assets/{units-page-CjtdlW8l.js → units-page-CpPFFKyE.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-C6Xxm9SA.js → vault-page-BsMf-9_W.js} +1 -1
- package/src/console/ui-next/dist/index.html +1 -1
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +36 -2
- package/src/drivers/cdc-outbox.ts +389 -0
- package/src/drivers/memory.ts +20 -0
- package/src/drivers/oauth-apple.ts +156 -0
- package/src/drivers/oauth-discord.ts +79 -0
- package/src/drivers/oauth-facebook.ts +80 -0
- package/src/drivers/oauth-figma.ts +116 -0
- package/src/drivers/oauth-github.ts +92 -0
- package/src/drivers/oauth-google.ts +142 -0
- package/src/drivers/oauth-microsoft.ts +174 -0
- package/src/drivers/oauth-oidc.ts +293 -0
- package/src/drivers/oauth-shared.ts +326 -0
- package/src/drivers/oauth-types.ts +159 -0
- package/src/drivers/oauth-x.ts +77 -0
- package/src/drivers/oauth2-common.ts +95 -0
- package/src/drivers/oauth2-token.ts +61 -0
- package/src/drivers/pg-rls-row-passes.ts +251 -0
- package/src/drivers/pg-rls.ts +2 -0
- package/src/drivers/postgres.ts +45 -2
- package/src/drivers/signal-postgres.ts +2 -1
- package/src/elements/channel/runtime.ts +29 -2
- package/src/elements/channel.test.ts +52 -0
- package/src/elements/gate/boot.ts +29 -2
- package/src/elements/store/emit-drizzle.ts +147 -14
- package/src/elements/store/field-ddl.test.ts +118 -0
- package/src/elements/store/field-types.test.ts +455 -0
- package/src/elements/store/list-query.golden.json +777 -0
- package/src/elements/store/list-query.parity.test.ts +396 -0
- package/src/elements/store/list-query.ts +792 -0
- package/src/elements/store/live-default.test.ts +136 -0
- package/src/elements/store/live-http.test.ts +160 -0
- package/src/elements/store/live-isolation.test.ts +291 -0
- package/src/elements/store/live-query-runtime.test.ts +323 -0
- package/src/elements/store/live-query-runtime.ts +403 -0
- package/src/elements/store/live-query-server.test.ts +377 -0
- package/src/elements/store/live-query-server.ts +102 -0
- package/src/elements/store/live-query.ts +97 -0
- package/src/elements/store/resource.ts +189 -680
- package/src/elements/store/rls-row-passes-policies.parity.test.ts +665 -0
- package/src/elements/store/schema-decl.ts +539 -41
- package/src/elements/store/sql-rls-stamp.test.ts +27 -0
- package/src/elements/store/sql-session.ts +297 -35
- package/src/elements/store/table.ts +102 -21
- package/src/elements/store.test.ts +3 -1
- package/src/elements/store.ts +12 -1
- package/src/elements/vault/chaos-child.ts +74 -1
- package/src/elements/vault/chaos.test.ts +4 -2
- package/src/elements/vault/storage.ts +4 -2
- package/src/index.ts +4 -1
- package/src/kernel/app-auth.ts +1 -0
- package/src/kernel/app.ts +116 -2
- package/src/kernel/auth-sharing.test.ts +196 -0
- package/src/kernel/boot.test.ts +3 -3
- package/src/kernel/errors.ts +8 -0
- package/src/kernel/fx.test.ts +1 -0
- package/src/kernel/fx.ts +14 -2
- package/src/kernel/horizontal-child.ts +2 -1
- package/src/kernel/http-resource.ts +33 -7
- package/src/kernel/identity-host-persist.test.ts +119 -0
- package/src/kernel/instance-id.ts +4 -2
- package/src/kernel/journal.ts +2 -1
- package/src/kernel/mcp-tool.test.ts +95 -0
- package/src/kernel/on.ts +9 -0
- package/src/kernel/realtime-bind.ts +326 -0
- package/src/kernel/resource-live.ts +117 -0
- package/src/kernel/triggers.ts +86 -4
- package/src/manifest/diff.ts +37 -0
- package/src/manifest/types.ts +64 -2
- package/src/okid.bench.test.ts +64 -0
- package/src/okid.test.ts +338 -0
- package/src/okid.ts +245 -0
- package/src/plugins/anonymous.ts +19 -1
- package/src/plugins/auth/shared.ts +15 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +10 -8
- package/src/plugins/mcp-oauth.ts +208 -0
- package/src/plugins/oauth/flow-store.ts +117 -0
- package/src/plugins/oauth/link.ts +69 -0
- package/src/plugins/oauth/shared.ts +108 -0
- package/src/plugins/oauth/token-vault.ts +100 -0
- package/src/plugins/oauth.security.test.ts +535 -0
- package/src/plugins/oauth.ts +532 -0
- package/src/plugins/otp.ts +48 -6
- package/src/plugins/passkey.ts +20 -1
- package/src/plugins/two-factor.ts +11 -0
- package/src/plugins/username.ts +40 -7
- package/src/release/build-lib.ts +7 -1
- package/src/release/measure.ts +1 -0
- package/src/release/official-plugins.ts +4 -1
- package/src/runs/collect.ts +2 -1
- package/src/runs/drivers/files.ts +2 -1
- package/src/test/create-test-app.ts +114 -5
- package/src/test/export-bundle.test.ts +33 -0
- package/src/test/live-signals.test.ts +83 -0
- package/src/test/tenant-isolation.test.ts +175 -0
- package/src/testing.ts +26 -0
- package/src/upgrade/codemods.ts +1 -1
- package/site/content/docs/reference/migrating-environments.mdx +0 -158
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Figma"
|
|
3
|
+
description: "Official plugin — Figma sign-in with HTTP Basic client auth and no email verification signal."
|
|
4
|
+
icon: "PenTool"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Figma is OAuth2 with two twists: the token endpoint uses **HTTP Basic**
|
|
9
|
+
instead of a body field, and the API exposes **no verification flag** — so
|
|
10
|
+
emails are always unverified.
|
|
11
|
+
|
|
12
|
+
<Callout title="The one rule">
|
|
13
|
+
Register your exact callback URI in the Figma app, seed the client secret, and expect unverified
|
|
14
|
+
emails — new accounts only, never takeovers.
|
|
15
|
+
</Callout>
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
|
|
21
|
+
<Step>
|
|
22
|
+
### Create an app
|
|
23
|
+
|
|
24
|
+
Figma → Settings → Security → **Personal access tokens / OAuth apps** → create
|
|
25
|
+
an OAuth app. Add `https://app.example.com/auth/oauth/callback/figma` as a
|
|
26
|
+
callback URL.
|
|
27
|
+
|
|
28
|
+
</Step>
|
|
29
|
+
|
|
30
|
+
<Step>
|
|
31
|
+
### Plug it
|
|
32
|
+
|
|
33
|
+
```typescript title="src/app.ts"
|
|
34
|
+
import { oke } from "okengine";
|
|
35
|
+
import { oauth } from "okengine/plugins";
|
|
36
|
+
|
|
37
|
+
export const app = oke({
|
|
38
|
+
name: "shop",
|
|
39
|
+
env: "dev",
|
|
40
|
+
gate: { auth: {} },
|
|
41
|
+
}).plug(
|
|
42
|
+
oauth({
|
|
43
|
+
baseUrl: "https://app.example.com",
|
|
44
|
+
providers: {
|
|
45
|
+
figma: { enabled: true },
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
</Step>
|
|
52
|
+
|
|
53
|
+
<Step>
|
|
54
|
+
### Set the client secret
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
# .env.local
|
|
58
|
+
OAUTH_FIGMA_CLIENT_SECRET=...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The driver sends it as `Authorization: Basic base64(client_id:client_secret)`
|
|
62
|
+
on the token call.
|
|
63
|
+
|
|
64
|
+
</Step>
|
|
65
|
+
|
|
66
|
+
</Steps>
|
|
67
|
+
|
|
68
|
+
## How identity works
|
|
69
|
+
|
|
70
|
+
| Aspect | Behavior |
|
|
71
|
+
| -------------- | --------------------------------------------------------------------------------- |
|
|
72
|
+
| Token exchange | `POST https://api.figma.com/v1/oauth/token` with the Basic header + PKCE verifier |
|
|
73
|
+
| Profile | `GET https://api.figma.com/v1/me` |
|
|
74
|
+
| Subject | `id`, falling back to `handle` when absent |
|
|
75
|
+
| Email trust | **always unverified** — no verification field exists |
|
|
76
|
+
|
|
77
|
+
Default scopes: `file_read`. Trim this to what you actually need; sign-in
|
|
78
|
+
itself requires nothing beyond defaults.
|
|
79
|
+
|
|
80
|
+
## Options
|
|
81
|
+
|
|
82
|
+
| Option | Type | Default | Meaning |
|
|
83
|
+
| --------------------- | ---------- | ------------------ | ------------------------- |
|
|
84
|
+
| `enabled` | `boolean` | `false` | Turn the provider on |
|
|
85
|
+
| `clientId` | `string` | Vault/env\* | `\*OAUTH_FIGMA_CLIENT_ID` |
|
|
86
|
+
| `redirectUri` | `string` | `{baseUrl}…/figma` | Exact registered URI |
|
|
87
|
+
| `scopes` | `string[]` | driver defaults | Extra scopes |
|
|
88
|
+
| `storeProviderTokens` | `boolean` | `false` | Keep tokens in Vault |
|
|
89
|
+
|
|
90
|
+
## Surfaces
|
|
91
|
+
|
|
92
|
+
| Flow | Path |
|
|
93
|
+
| -------- | ------------------------------------- |
|
|
94
|
+
| Start | `POST /auth/oauth/figma/start` |
|
|
95
|
+
| Callback | `GET+POST /auth/oauth/callback/figma` |
|
|
96
|
+
| Link | `POST /auth/oauth/figma/link` |
|
|
97
|
+
|
|
98
|
+
## Troubleshooting
|
|
99
|
+
|
|
100
|
+
<Accordions>
|
|
101
|
+
<Accordion title="401 on token exchange">
|
|
102
|
+
|
|
103
|
+
The Basic header is built from _your_ pair — a mismatched id/secret rotation
|
|
104
|
+
or a secret seeded under the wrong provider key. Check
|
|
105
|
+
`OAUTH_FIGMA_CLIENT_SECRET`.
|
|
106
|
+
|
|
107
|
+
</Accordion>
|
|
108
|
+
<Accordion title="invalid_grant">
|
|
109
|
+
|
|
110
|
+
Codes are single-use and expire quickly. Flow rows are too — restart from
|
|
111
|
+
`/start`.
|
|
112
|
+
|
|
113
|
+
</Accordion>
|
|
114
|
+
<Accordion title="Email missing on the profile">
|
|
115
|
+
|
|
116
|
+
Some Figma accounts expose no email. The session signs in without one;
|
|
117
|
+
nothing else changes.
|
|
118
|
+
|
|
119
|
+
</Accordion>
|
|
120
|
+
</Accordions>
|
|
121
|
+
|
|
122
|
+
## Learn more
|
|
123
|
+
|
|
124
|
+
- [OAuth](/docs/plugins/oauth) — shared flows and security model
|
|
125
|
+
- [GitHub](/docs/plugins/github) — OAuth2 with real verification signal
|
|
126
|
+
- [Vault](/docs/elements/vault) — where secrets live
|
|
127
|
+
|
|
128
|
+
## Next
|
|
129
|
+
|
|
130
|
+
<Cards>
|
|
131
|
+
<Card title="Google" description="OIDC reference provider." href="/docs/plugins/google" />
|
|
132
|
+
<Card
|
|
133
|
+
title="GitHub"
|
|
134
|
+
description="OAuth2 with verified-email lookup."
|
|
135
|
+
href="/docs/plugins/github"
|
|
136
|
+
/>
|
|
137
|
+
<Card title="X" description="PKCE public client." href="/docs/plugins/x" />
|
|
138
|
+
</Cards>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "GitHub"
|
|
3
|
+
description: "Official plugin — GitHub sign-in with primary-email lookup from the emails API."
|
|
4
|
+
icon: "GitBranch"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
GitHub is OAuth2 without discovery or ID tokens, so `oauth()` builds the
|
|
9
|
+
identity assertion from two REST calls: `/user` for the account and
|
|
10
|
+
`/user/emails` for the address that actually matters.
|
|
11
|
+
|
|
12
|
+
<Callout title="The one rule">
|
|
13
|
+
Request the `user:email` scope. The public profile email is usually null; the verified primary
|
|
14
|
+
address only exists on the emails endpoint.
|
|
15
|
+
</Callout>
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
|
|
21
|
+
<Step>
|
|
22
|
+
### Create an OAuth App
|
|
23
|
+
|
|
24
|
+
GitHub → Settings → Developer settings → OAuth Apps → **New OAuth App**.
|
|
25
|
+
Authorization callback URL:
|
|
26
|
+
`https://app.example.com/auth/oauth/callback/github`.
|
|
27
|
+
|
|
28
|
+
</Step>
|
|
29
|
+
|
|
30
|
+
<Step>
|
|
31
|
+
### Plug it
|
|
32
|
+
|
|
33
|
+
```typescript title="src/app.ts"
|
|
34
|
+
import { oke } from "okengine";
|
|
35
|
+
import { oauth } from "okengine/plugins";
|
|
36
|
+
|
|
37
|
+
export const app = oke({
|
|
38
|
+
name: "shop",
|
|
39
|
+
env: "dev",
|
|
40
|
+
gate: { auth: {} },
|
|
41
|
+
}).plug(
|
|
42
|
+
oauth({
|
|
43
|
+
baseUrl: "https://app.example.com",
|
|
44
|
+
providers: {
|
|
45
|
+
github: { enabled: true },
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
</Step>
|
|
52
|
+
|
|
53
|
+
<Step>
|
|
54
|
+
### Set the client secret
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
# .env.local
|
|
58
|
+
OAUTH_GITHUB_CLIENT_SECRET=...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
</Step>
|
|
62
|
+
|
|
63
|
+
</Steps>
|
|
64
|
+
|
|
65
|
+
## How identity works
|
|
66
|
+
|
|
67
|
+
| Aspect | Behavior |
|
|
68
|
+
| -------------- | ------------------------------------------------------------------------------ |
|
|
69
|
+
| Token exchange | form POST to `github.com/login/oauth/access_token`, JSON accepted |
|
|
70
|
+
| Profile | `GET https://api.github.com/user` (numeric `id` is the subject) |
|
|
71
|
+
| Email | `GET https://api.github.com/user/emails`; picks the entry with `primary: true` |
|
|
72
|
+
| Email trust | verified only when the selected entry has an explicit `verified: true` |
|
|
73
|
+
|
|
74
|
+
**Consequence:** a GitHub account whose primary email is unverified never
|
|
75
|
+
claims that address during sign-up — the flow provisions without verified
|
|
76
|
+
status instead of risking someone else's inbox.
|
|
77
|
+
|
|
78
|
+
Default scopes: `read:user`, `user:email`. PKCE parameters are sent; GitHub
|
|
79
|
+
ignores them but the protection stays uniform across providers.
|
|
80
|
+
|
|
81
|
+
## Options
|
|
82
|
+
|
|
83
|
+
| Option | Type | Default | Meaning |
|
|
84
|
+
| --------------------- | ---------- | ------------------- | -------------------------- |
|
|
85
|
+
| `enabled` | `boolean` | `false` | Turn the provider on |
|
|
86
|
+
| `clientId` | `string` | Vault/env\* | `\*OAUTH_GITHUB_CLIENT_ID` |
|
|
87
|
+
| `redirectUri` | `string` | `{baseUrl}…/github` | Exact registered URI |
|
|
88
|
+
| `scopes` | `string[]` | driver defaults | Extra scopes |
|
|
89
|
+
| `storeProviderTokens` | `boolean` | `false` | Keep tokens in Vault |
|
|
90
|
+
|
|
91
|
+
## Surfaces
|
|
92
|
+
|
|
93
|
+
| Flow | Path |
|
|
94
|
+
| -------- | -------------------------------------- |
|
|
95
|
+
| Start | `POST /auth/oauth/github/start` |
|
|
96
|
+
| Callback | `GET+POST /auth/oauth/callback/github` |
|
|
97
|
+
| Link | `POST /auth/oauth/github/link` |
|
|
98
|
+
|
|
99
|
+
## Troubleshooting
|
|
100
|
+
|
|
101
|
+
<Accordions>
|
|
102
|
+
<Accordion title="Sign-in succeeds with no email">
|
|
103
|
+
|
|
104
|
+
The token lacks `user:email` (custom scopes dropped it) or the account has no
|
|
105
|
+
verified addresses. The session is valid; add the scope for future flows.
|
|
106
|
+
|
|
107
|
+
</Accordion>
|
|
108
|
+
<Accordion title="401 on /user/emails">
|
|
109
|
+
|
|
110
|
+
App permissions changed or the user revoked the grant under Settings →
|
|
111
|
+
Applications. The driver degrades to no email rather than failing sign-in.
|
|
112
|
+
|
|
113
|
+
</Accordion>
|
|
114
|
+
<Accordion title="redirect_uri mismatch">
|
|
115
|
+
|
|
116
|
+
GitHub compares the registered callback URL exactly. Re-copy the value your
|
|
117
|
+
config produces.
|
|
118
|
+
|
|
119
|
+
</Accordion>
|
|
120
|
+
</Accordions>
|
|
121
|
+
|
|
122
|
+
## Learn more
|
|
123
|
+
|
|
124
|
+
- [OAuth](/docs/plugins/oauth) — shared flows and security model
|
|
125
|
+
- [Discord](/docs/plugins/discord) — same OAuth2 shape, nullable email
|
|
126
|
+
- [Vault](/docs/elements/vault) — where secrets live
|
|
127
|
+
|
|
128
|
+
## Next
|
|
129
|
+
|
|
130
|
+
<Cards>
|
|
131
|
+
<Card
|
|
132
|
+
title="Discord"
|
|
133
|
+
description="Verified flag with nullable email."
|
|
134
|
+
href="/docs/plugins/discord"
|
|
135
|
+
/>
|
|
136
|
+
<Card title="Google" description="OIDC reference provider." href="/docs/plugins/google" />
|
|
137
|
+
<Card title="X" description="PKCE public client." href="/docs/plugins/x" />
|
|
138
|
+
</Cards>
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Google"
|
|
3
|
+
description: "Official plugin — Google sign-in via OIDC discovery and JWKS-verified ID tokens."
|
|
4
|
+
icon: "Globe"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Google is the reference OIDC provider for `oauth()`: discovery, ID-token
|
|
9
|
+
signature checks, and issuer pinning all work the textbook way. If you are
|
|
10
|
+
wiring your first social provider, start here.
|
|
11
|
+
|
|
12
|
+
<Callout title="The one rule">
|
|
13
|
+
Register `https://app.example.com/auth/oauth/callback/google` (your exact origin) as an Authorized
|
|
14
|
+
Redirect URI, then enable it with `providers.google.enabled`. The comparison is byte-exact.
|
|
15
|
+
</Callout>
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
|
|
21
|
+
<Step>
|
|
22
|
+
### Create OAuth credentials
|
|
23
|
+
|
|
24
|
+
In Google Cloud Console → APIs & Services → Credentials, create an **OAuth
|
|
25
|
+
client ID** of type _Web application_. Add your callback URI under Authorized
|
|
26
|
+
Redirect URIs.
|
|
27
|
+
|
|
28
|
+
</Step>
|
|
29
|
+
|
|
30
|
+
<Step>
|
|
31
|
+
### Plug it
|
|
32
|
+
|
|
33
|
+
```typescript title="src/app.ts"
|
|
34
|
+
import { oke } from "okengine";
|
|
35
|
+
import { oauth } from "okengine/plugins";
|
|
36
|
+
|
|
37
|
+
export const app = oke({
|
|
38
|
+
name: "shop",
|
|
39
|
+
env: "dev",
|
|
40
|
+
gate: { auth: {} },
|
|
41
|
+
}).plug(
|
|
42
|
+
oauth({
|
|
43
|
+
baseUrl: "https://app.example.com",
|
|
44
|
+
providers: {
|
|
45
|
+
google: { enabled: true },
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
</Step>
|
|
52
|
+
|
|
53
|
+
<Step>
|
|
54
|
+
### Set the client secret
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
# .env.local
|
|
58
|
+
OAUTH_GOOGLE_CLIENT_SECRET=GOCSPX-...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Boot fails loudly if the secret is missing — the contract is declared by the
|
|
62
|
+
plugin itself.
|
|
63
|
+
|
|
64
|
+
</Step>
|
|
65
|
+
|
|
66
|
+
</Steps>
|
|
67
|
+
|
|
68
|
+
## How identity works
|
|
69
|
+
|
|
70
|
+
The driver discovers Google's endpoints from
|
|
71
|
+
`https://accounts.google.com/.well-known/openid-configuration` and caches them.
|
|
72
|
+
On callback it verifies the ID token end-to-end:
|
|
73
|
+
|
|
74
|
+
| Check | Rule |
|
|
75
|
+
| --------- | ------------------------------------------------------- |
|
|
76
|
+
| Signature | RS256 / ES256 against Google's published JWKS |
|
|
77
|
+
| Issuer | must equal `https://accounts.google.com` |
|
|
78
|
+
| Audience | must include your client id (`azp` when multi-audience) |
|
|
79
|
+
| Expiry | rejected when stale |
|
|
80
|
+
| Nonce | single-use, bound to the flow row |
|
|
81
|
+
|
|
82
|
+
Email trust follows the OIDC claim: `email_verified: true` marks the address
|
|
83
|
+
verified; anything else — including string `"false"` — stays unverified.
|
|
84
|
+
|
|
85
|
+
Default scopes: `openid`, `email`, `profile`.
|
|
86
|
+
|
|
87
|
+
## Options
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Meaning |
|
|
90
|
+
| --------------------- | ---------- | ------------------- | -------------------------- |
|
|
91
|
+
| `enabled` | `boolean` | `false` | Turn the provider on |
|
|
92
|
+
| `clientId` | `string` | Vault/env\* | `\*OAUTH_GOOGLE_CLIENT_ID` |
|
|
93
|
+
| `redirectUri` | `string` | `{baseUrl}…/google` | Exact registered URI |
|
|
94
|
+
| `scopes` | `string[]` | driver defaults | Extra scopes |
|
|
95
|
+
| `storeProviderTokens` | `boolean` | `false` | Keep tokens in Vault |
|
|
96
|
+
|
|
97
|
+
## Surfaces
|
|
98
|
+
|
|
99
|
+
| Flow | Path |
|
|
100
|
+
| -------- | -------------------------------------- |
|
|
101
|
+
| Start | `POST /auth/oauth/google/start` |
|
|
102
|
+
| Callback | `GET+POST /auth/oauth/callback/google` |
|
|
103
|
+
| Link | `POST /auth/oauth/google/link` |
|
|
104
|
+
|
|
105
|
+
## Troubleshooting
|
|
106
|
+
|
|
107
|
+
<Accordions>
|
|
108
|
+
<Accordion title="redirect_uri_mismatch at Google">
|
|
109
|
+
|
|
110
|
+
The registered URI and the sent URI differ. Byte-exact means scheme, host,
|
|
111
|
+
port, path, no trailing slash drift. Copy the value from
|
|
112
|
+
`providers.google.redirectUri` or `baseUrl` synthesis.
|
|
113
|
+
|
|
114
|
+
</Accordion>
|
|
115
|
+
<Accordion title="invalid_client at token exchange">
|
|
116
|
+
|
|
117
|
+
`OAUTH_GOOGLE_CLIENT_SECRET` is missing or stale. Secrets resolve through the
|
|
118
|
+
Vault chain — update `.env.local` or your Vault driver, then restart.
|
|
119
|
+
|
|
120
|
+
</Accordion>
|
|
121
|
+
<Accordion title="aud_mismatch">
|
|
122
|
+
|
|
123
|
+
You are using credentials from a different Google project than the one that
|
|
124
|
+
issued the code, or swapped client ids between environments.
|
|
125
|
+
|
|
126
|
+
</Accordion>
|
|
127
|
+
</Accordions>
|
|
128
|
+
|
|
129
|
+
## Learn more
|
|
130
|
+
|
|
131
|
+
- [OAuth](/docs/plugins/oauth) — shared flows and security model
|
|
132
|
+
- [Vault](/docs/elements/vault) — where secrets live
|
|
133
|
+
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
134
|
+
|
|
135
|
+
## Next
|
|
136
|
+
|
|
137
|
+
<Cards>
|
|
138
|
+
<Card
|
|
139
|
+
title="Apple"
|
|
140
|
+
description="form_post + ES256 client-secret JWT."
|
|
141
|
+
href="/docs/plugins/apple"
|
|
142
|
+
/>
|
|
143
|
+
<Card
|
|
144
|
+
title="Microsoft"
|
|
145
|
+
description="Entra tenants and issuer templates."
|
|
146
|
+
href="/docs/plugins/microsoft"
|
|
147
|
+
/>
|
|
148
|
+
<Card
|
|
149
|
+
title="GitHub"
|
|
150
|
+
description="OAuth2 with verified-email lookup."
|
|
151
|
+
href="/docs/plugins/github"
|
|
152
|
+
/>
|
|
153
|
+
</Cards>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Plugins"
|
|
3
|
-
description: "Official okengine/plugins extensions — authentication, security, operations, and performance."
|
|
3
|
+
description: "Official okengine/plugins extensions — authentication, OAuth, security, operations, and performance."
|
|
4
4
|
icon: "Puzzle"
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
@@ -22,6 +22,52 @@ First-party plugins you `.plug()` onto an app. Each page is one export from `oke
|
|
|
22
22
|
<Card title="Passkey" description="WebAuthn-shaped passkeys." href="/docs/plugins/passkey" />
|
|
23
23
|
</Cards>
|
|
24
24
|
|
|
25
|
+
## OAuth
|
|
26
|
+
|
|
27
|
+
<Cards>
|
|
28
|
+
<Card
|
|
29
|
+
title="OAuth"
|
|
30
|
+
description="Social sign-in — Authorization Code + PKCE for eight providers."
|
|
31
|
+
href="/docs/plugins/oauth"
|
|
32
|
+
/>
|
|
33
|
+
<Card
|
|
34
|
+
title="Apple"
|
|
35
|
+
description="Sign in with Apple — form_post + ES256 JWT."
|
|
36
|
+
href="/docs/plugins/apple"
|
|
37
|
+
/>
|
|
38
|
+
<Card
|
|
39
|
+
title="Discord"
|
|
40
|
+
description="Discord OAuth2 with nullable email."
|
|
41
|
+
href="/docs/plugins/discord"
|
|
42
|
+
/>
|
|
43
|
+
<Card
|
|
44
|
+
title="Facebook"
|
|
45
|
+
description="Facebook Login — never-verified emails."
|
|
46
|
+
href="/docs/plugins/facebook"
|
|
47
|
+
/>
|
|
48
|
+
<Card
|
|
49
|
+
title="Figma"
|
|
50
|
+
description="Figma OAuth2 with HTTP Basic token auth."
|
|
51
|
+
href="/docs/plugins/figma"
|
|
52
|
+
/>
|
|
53
|
+
<Card
|
|
54
|
+
title="GitHub"
|
|
55
|
+
description="GitHub OAuth2 with verified-email lookup."
|
|
56
|
+
href="/docs/plugins/github"
|
|
57
|
+
/>
|
|
58
|
+
<Card
|
|
59
|
+
title="Google"
|
|
60
|
+
description="Google OIDC — JWKS-verified ID tokens."
|
|
61
|
+
href="/docs/plugins/google"
|
|
62
|
+
/>
|
|
63
|
+
<Card
|
|
64
|
+
title="Microsoft"
|
|
65
|
+
description="Entra ID OIDC with tenant-aware issuer checks."
|
|
66
|
+
href="/docs/plugins/microsoft"
|
|
67
|
+
/>
|
|
68
|
+
<Card title="X" description="X OAuth2 public client — PKCE only." href="/docs/plugins/x" />
|
|
69
|
+
</Cards>
|
|
70
|
+
|
|
25
71
|
## Security
|
|
26
72
|
|
|
27
73
|
<Cards>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Microsoft"
|
|
3
|
+
description: "Official plugin — Entra ID sign-in across personal, work, and school accounts with tenant-aware issuer checks."
|
|
4
|
+
icon: "LayoutGrid"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Microsoft (Entra ID) is OIDC with a tenant twist: `common` discovery advertises
|
|
9
|
+
an issuer with the `{tenantid}` placeholder, so the value you pin at start is
|
|
10
|
+
a _template_ validated against the concrete token at callback.
|
|
11
|
+
|
|
12
|
+
<Callout title="The one rule">
|
|
13
|
+
Pick the audience first: `tenant: "common"` for everyone, `organizations` for work/school only,
|
|
14
|
+
`consumers` for personal accounts, or a tenant GUID to lock sign-in to one directory.
|
|
15
|
+
</Callout>
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
|
|
21
|
+
<Step>
|
|
22
|
+
### Register an app
|
|
23
|
+
|
|
24
|
+
In the Azure portal → Microsoft Entra ID → App registrations → New
|
|
25
|
+
registration. Choose _Accounts in any organizational directory and personal
|
|
26
|
+
Microsoft accounts_ for `common`. Add your redirect URI under **Web**.
|
|
27
|
+
|
|
28
|
+
</Step>
|
|
29
|
+
|
|
30
|
+
<Step>
|
|
31
|
+
### Plug it
|
|
32
|
+
|
|
33
|
+
```typescript title="src/app.ts"
|
|
34
|
+
import { oke } from "okengine";
|
|
35
|
+
import { oauth } from "okengine/plugins";
|
|
36
|
+
|
|
37
|
+
export const app = oke({
|
|
38
|
+
name: "shop",
|
|
39
|
+
env: "dev",
|
|
40
|
+
gate: { auth: {} },
|
|
41
|
+
}).plug(
|
|
42
|
+
oauth({
|
|
43
|
+
baseUrl: "https://app.example.com",
|
|
44
|
+
providers: {
|
|
45
|
+
microsoft: { enabled: true }, // tenant defaults to "common"
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
</Step>
|
|
52
|
+
|
|
53
|
+
<Step>
|
|
54
|
+
### Set the client secret
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
# .env.local
|
|
58
|
+
OAUTH_MICROSOFT_CLIENT_SECRET=...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Certificates are not used — the driver authenticates with the shared secret
|
|
62
|
+
form field.
|
|
63
|
+
|
|
64
|
+
</Step>
|
|
65
|
+
|
|
66
|
+
</Steps>
|
|
67
|
+
|
|
68
|
+
## How identity works
|
|
69
|
+
|
|
70
|
+
| Aspect | Behavior |
|
|
71
|
+
| -------------- | ----------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| Discovery | `{tenant}/oauth2/v2.0/.well-known/openid-configuration`, cached |
|
|
73
|
+
| Issuer pinning | flow stores `https://login.microsoftonline.com/{tenantid}/v2.0`; tokens must match it shape-for-shape |
|
|
74
|
+
| Tenant claim | the token's `tid` must be GUID-shaped and consistent with `iss` |
|
|
75
|
+
| Signature | RS256 against the tenant's published keys |
|
|
76
|
+
| Email trust | OIDC `email_verified` claim |
|
|
77
|
+
|
|
78
|
+
**Consequence:** an ID token minted by a _different_ tenant fails the issuer
|
|
79
|
+
template even though its signature is perfectly valid — the mix-up defense
|
|
80
|
+
survives multi-tenancy.
|
|
81
|
+
|
|
82
|
+
Default scopes: `openid`, `email`, `profile`.
|
|
83
|
+
|
|
84
|
+
## Options
|
|
85
|
+
|
|
86
|
+
| Option | Type | Default | Meaning |
|
|
87
|
+
| --------------------- | ---------- | ---------------------- | --------------------------------------- |
|
|
88
|
+
| `enabled` | `boolean` | `false` | Turn the provider on |
|
|
89
|
+
| `clientId` | `string` | Vault/env\* | `\*OAUTH_MICROSOFT_CLIENT_ID` |
|
|
90
|
+
| `tenant` | `string` | `"common"` | `organizations`, `consumers`, or a GUID |
|
|
91
|
+
| `redirectUri` | `string` | `{baseUrl}…/microsoft` | Exact registered URI |
|
|
92
|
+
| `scopes` | `string[]` | driver defaults | Extra scopes |
|
|
93
|
+
| `storeProviderTokens` | `boolean` | `false` | Keep tokens in Vault |
|
|
94
|
+
|
|
95
|
+
## Surfaces
|
|
96
|
+
|
|
97
|
+
| Flow | Path |
|
|
98
|
+
| -------- | ----------------------------------------- |
|
|
99
|
+
| Start | `POST /auth/oauth/microsoft/start` |
|
|
100
|
+
| Callback | `GET+POST /auth/oauth/callback/microsoft` |
|
|
101
|
+
| Link | `POST /auth/oauth/microsoft/link` |
|
|
102
|
+
|
|
103
|
+
## Troubleshooting
|
|
104
|
+
|
|
105
|
+
<Accordions>
|
|
106
|
+
<Accordion title="issuer_mismatch on every login">
|
|
107
|
+
|
|
108
|
+
Your app registration's _supported account types_ disagree with the configured
|
|
109
|
+
`tenant`. `common` requires multi-tenant + personal accounts; a single-tenant
|
|
110
|
+
GUID needs the matching registration.
|
|
111
|
+
|
|
112
|
+
</Accordion>
|
|
113
|
+
<Accordion title="AADSTS50011 (redirect mismatch)">
|
|
114
|
+
|
|
115
|
+
The reply URL registered in Azure does not byte-match the stored
|
|
116
|
+
`redirectUri`. Re-copy from your plugin config; trailing slashes count.
|
|
117
|
+
|
|
118
|
+
</Accordion>
|
|
119
|
+
<Accordion title="No email arrives">
|
|
120
|
+
|
|
121
|
+
Personal Microsoft accounts can omit email entirely. The flow signs the person
|
|
122
|
+
in without one — exactly like Discord phone-only accounts.
|
|
123
|
+
|
|
124
|
+
</Accordion>
|
|
125
|
+
</Accordions>
|
|
126
|
+
|
|
127
|
+
## Learn more
|
|
128
|
+
|
|
129
|
+
- [OAuth](/docs/plugins/oauth) — shared flows and security model
|
|
130
|
+
- [Google](/docs/plugins/google) — plain single-issuer OIDC
|
|
131
|
+
- [Vault](/docs/elements/vault) — where secrets live
|
|
132
|
+
|
|
133
|
+
## Next
|
|
134
|
+
|
|
135
|
+
<Cards>
|
|
136
|
+
<Card
|
|
137
|
+
title="Apple"
|
|
138
|
+
description="form_post + ES256 client-secret JWT."
|
|
139
|
+
href="/docs/plugins/apple"
|
|
140
|
+
/>
|
|
141
|
+
<Card
|
|
142
|
+
title="Discord"
|
|
143
|
+
description="Nullable emails, verified flag."
|
|
144
|
+
href="/docs/plugins/discord"
|
|
145
|
+
/>
|
|
146
|
+
<Card
|
|
147
|
+
title="GitHub"
|
|
148
|
+
description="OAuth2 with verified-email lookup."
|
|
149
|
+
href="/docs/plugins/github"
|
|
150
|
+
/>
|
|
151
|
+
</Cards>
|