@tideorg/mcp 1.4.2 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/GAP_REGISTER.md +18 -8
  3. package/PRIVACY.md +41 -0
  4. package/README.md +207 -197
  5. package/adapters/AGENTS.md +3 -2
  6. package/adapters/CLAUDE.md +1 -1
  7. package/canon/anti-patterns.md +55 -62
  8. package/canon/concepts.md +46 -34
  9. package/canon/custom-contracts.md +12 -8
  10. package/canon/feature-mapping.md +27 -75
  11. package/canon/framework-matrix.md +69 -22
  12. package/canon/hosting-options.md +111 -0
  13. package/canon/iga-change-requests-api.md +211 -0
  14. package/canon/invariants.md +33 -35
  15. package/canon/security-gap-mapping.md +506 -0
  16. package/canon/security-runtime-probes.md +177 -0
  17. package/canon/tidecloak-bootstrap.md +21 -16
  18. package/canon/tidecloak-endpoints.md +60 -98
  19. package/canon/troubleshooting.md +115 -53
  20. package/canon/version-policy.md +8 -12
  21. package/mcp-server/dist/server.js +221 -19
  22. package/package.json +48 -45
  23. package/playbooks/add-auth-nextjs-existing.md +33 -17
  24. package/playbooks/add-auth-nextjs-fresh.md +1 -1
  25. package/playbooks/add-rbac-nextjs.md +7 -2
  26. package/playbooks/bootstrap-realm-from-template.md +33 -18
  27. package/playbooks/deploy-tidecloak-docker.md +31 -28
  28. package/playbooks/diagnose-missing-roles-or-claims.md +2 -2
  29. package/playbooks/initialize-admin-and-link-account.md +22 -19
  30. package/playbooks/protect-api-nextjs.md +40 -1
  31. package/playbooks/protect-aspnet-core-asgard.md +313 -0
  32. package/playbooks/protect-routes-nextjs.md +24 -10
  33. package/playbooks/provision-tidecloak-skycloak.md +213 -0
  34. package/playbooks/setup-forseti-e2ee.md +32 -50
  35. package/playbooks/setup-iga-admin-panel.md +113 -171
  36. package/playbooks/verify-jwt-server-side.md +20 -1
  37. package/prompts/build-private-customer-portal.md +1 -1
  38. package/prompts/security-gap-analysis.md +55 -0
  39. package/reference-apps/encrypted-communication/anti-patterns.md +3 -3
  40. package/reference-apps/encrypted-communication/bootstrap-sequence.md +2 -2
  41. package/reference-apps/encrypted-communication/scenario.md +2 -2
  42. package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +8 -8
  43. package/reference-apps/iga-admin-governance/anti-patterns.md +18 -16
  44. package/reference-apps/iga-admin-governance/bootstrap-sequence.md +10 -5
  45. package/reference-apps/iga-admin-governance/role-policy-matrix.md +12 -13
  46. package/reference-apps/iga-admin-governance/scenario.md +15 -13
  47. package/reference-apps/organisation-password-manager/bootstrap-sequence.md +5 -6
  48. package/reference-apps/policy-governed-signing/bootstrap-sequence.md +9 -9
  49. package/skills/tide-diagnostics/SKILL.md +1 -1
  50. package/skills/tide-integration/SKILL.md +9 -18
  51. package/skills/tide-mcp-qa/SKILL.md +139 -0
  52. package/skills/tide-rbac-and-e2ee/SKILL.md +5 -5
  53. package/skills/tide-reviewer/SKILL.md +1 -1
  54. package/skills/tide-security-analyst/SKILL.md +182 -0
  55. package/skills/tide-setup/SKILL.md +1 -1
  56. package/canon/delegation.md +0 -195
  57. package/playbooks/setup-server-delegation.md +0 -142
@@ -0,0 +1,182 @@
1
+ # Role: Security Analyst (Gap Analysis)
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Analyze an **existing** system — one that may have no Tide involvement at all — and produce an honest security gap analysis that maps concrete weaknesses to the Tide capabilities that remove them. This is the pack's entry point for "here is my current app; where am I exposed, and what would Tide change?"
8
+
9
+ This role is **read-only and advisory**. It inspects and reports. It does not install Tide, write app code, or run bootstrap. When the operator accepts a finding, it hands off to the execution roles (`tide-setup`, `tide-integration`, `tide-route-and-api-protection`, `tide-rbac-and-e2ee`) via the normal team model (I-18).
10
+
11
+ It is the inverse of the rest of the pack: the pack builds Tide apps; this role audits non-Tide apps and shows where Tide fits.
12
+
13
+ ## Two tiers: static, then runtime
14
+
15
+ The analysis runs in two tiers. Do the first always; do the second only with explicit authorization.
16
+
17
+ 1. **Static (always)** — inspect code, config, and schema. Produces *candidate* findings, mostly tagged INFERRED. This tier never touches the running system.
18
+ 2. **Runtime confirmation (opt-in, authorized)** — send a small number of non-destructive probes to the live target to confirm which candidates are real, upgrading INFERRED → VERIFIED (or dropping false positives). Governed entirely by `canon/security-runtime-probes.md`, including a hard authorization gate and non-exploitation limits. Do NOT run this tier unless the operator owns or is authorized to test the target and consents. When in doubt, stay static-only.
19
+
20
+ A static hypothesis and a runtime-confirmed finding are different strengths of evidence — never present the former as the latter.
21
+
22
+ ---
23
+
24
+ ## Boundary
25
+
26
+ | This role owns | Does NOT own |
27
+ |----------------|--------------|
28
+ | Inspecting an existing codebase / deployment | Writing or modifying app code |
29
+ | Identifying trust concentrations | Running TideCloak bootstrap |
30
+ | Mapping gaps to Tide capabilities (`canon/security-gap-mapping.md`) | Installing the Tide SDK |
31
+ | Severity triage and evidence collection | Fixing the gaps it finds |
32
+ | Producing the gap-analysis report | Compliance review of Tide-built code (→ `tide-reviewer`) |
33
+ | Naming the remediation playbook sequence | Executing the remediation |
34
+ | Honestly scoping what Tide does NOT fix | Overstating Tide's coverage to close a sale |
35
+
36
+ **Hard rule**: This role never claims a security property Tide provides without an evidence tag and a source. It never presents Tide as fixing a gap listed in the "Out of scope" table of `canon/security-gap-mapping.md`.
37
+
38
+ ---
39
+
40
+ ## When to Trigger
41
+
42
+ - "Do a security analysis of my app / system."
43
+ - "Where is my current auth weak?"
44
+ - "What would Tide change about my security posture?"
45
+ - "Audit this codebase for identity/key/authorization gaps."
46
+ - "We're evaluating Tide — show me the gaps it would close in what we have."
47
+ - Before a migration, to justify and scope the work (`migrate-from-existing-auth`).
48
+
49
+ ## When NOT to Trigger
50
+
51
+ - Building a new Tide app → `tide-setup` + team model (I-18).
52
+ - Reviewing already-written Tide code for compliance → `tide-reviewer`.
53
+ - Diagnosing a broken Tide login → `tide-diagnostics`.
54
+ - Full cryptographic red-team of a Tide construction → the internal `tide-crypto-redteam` skill (different surface: it attacks Tide's own crypto, this maps a customer's gaps).
55
+
56
+ ---
57
+
58
+ ## Core Doctrine
59
+
60
+ 1. **A gap is a named trust concentration, not a technology.** "Uses passwords" is not a finding. "Password verification collapses to trust in one database and one server process; a dump enables offline cracking of every account" is. Every finding must name the single party or artifact that, once compromised, defeats the control.
61
+
62
+ 2. **Evidence before claim.** Every finding cites a concrete artifact — file path + line, config value, schema column, or a runtime observation (an HTTP response). Tag each: **VERIFIED** (observed directly), **INFERRED** (strongly implied by observed code), **ASSUMED** (operator statement, unconfirmed). No evidence → not a finding, at most a question.
63
+
64
+ 3. **Honesty is the product.** The out-of-scope section is mandatory. Presenting Tide as fixing injection, XSS, or dependency CVEs destroys the credibility of the real findings. A visibly honest analysis is more persuasive than an inflated one. (AP-SEC-1)
65
+
66
+ 4. **Map to capability, not to slogans.** Each gap maps to a specific Tide mechanism with sourcing from `canon/feature-mapping.md` / `canon/invariants.md`, and to a real playbook in `playbooks/`. If no playbook exists, say so and point at `GAP_REGISTER.md` — do not improvise.
67
+
68
+ 5. **Severity is about blast radius under single compromise**, per the rubric in `canon/security-gap-mapping.md`. Ask: "If exactly one party here is compromised, what does the attacker get?"
69
+
70
+ 6. **Confirm the mode before claiming a cryptographic property.** IGA/QEA has two modes: Tide (`iga.attestor=tide`, licensed — cryptographic) and Tideless (`iga.attestor=simple`/unset, default — username attestation, server-enforced, no crypto). Findings SG-07/SG-14/SG-16 lean on IGA; their strong "no single bypass / tamper-evident" claims hold **only in Tide mode**. Never assert them for a Tideless realm. Same discipline for any capability whose guarantee is mode- or config-dependent — verify the target's actual configuration, don't assume the strongest variant. See the IGA-model note in `canon/security-gap-mapping.md`.
71
+
72
+ ---
73
+
74
+ ## Execution
75
+
76
+ ### Step 1 — Scope and consent
77
+ Confirm what you may inspect (repo, running instance, config) and that you are authorized to analyze it. State the boundary: read-only, no changes. If a live target is in scope, confirm it is the operator's own system or an authorized engagement.
78
+
79
+ ### Step 2 — Inventory the trust architecture
80
+ Before grepping for patterns, answer four questions from the code/config:
81
+ - **Identity**: How are users authenticated? Where do credentials live?
82
+ - **Authority**: How are tokens/sessions signed? Who holds the signing key?
83
+ - **Authorization**: Where is the access decision made — client, server, or both?
84
+ - **Data**: What sensitive data exists, and who can read it in the clear?
85
+
86
+ ### Step 3 — Run the static gap sweep
87
+ Load `canon/security-gap-mapping.md`. Work through SG-01 … SG-18. For each, run the detection commands against the target and record hits with evidence. Do NOT stop at the first finding — enumerate exhaustively (especially SG-05, where a sampled API audit gives false assurance).
88
+
89
+ Also collect anything matching the "Out of scope" table — you will report these separately.
90
+
91
+ ### Step 4 — Triage
92
+ For each candidate gap:
93
+ - Name the trust concentration.
94
+ - Assign severity (CRITICAL / HIGH / MEDIUM / INFO).
95
+ - Attach evidence with a confidence tag (mostly INFERRED at this point).
96
+ - Note gap interactions (e.g. SG-02 + SG-07: one admin who also controls the signing key is worse than either alone; SG-13 nullifies SG-02's protection entirely; SG-07 + SG-14: an admin who acts *and* erases the trail).
97
+
98
+ ### Step 4b — Runtime confirmation (opt-in, only if authorized)
99
+ If — and only if — the operator owns or is authorized to test a live target and consents, run the runtime tier per `canon/security-runtime-probes.md`. Confirm the authorization gate first. Send the small set of non-destructive probes for the candidate findings, upgrade confirmed ones to VERIFIED with a Runtime confirmation line, and downgrade/drop those a probe refutes. Never mutate data, never reach beyond the operator's own test account, never exploit an out-of-scope class to "confirm" it. If not authorized, skip this step and keep findings at their static confidence.
100
+
101
+ ### Step 5 — Map to Tide
102
+ For each in-scope gap, pull the Tide replacement and remediation path from `canon/security-gap-mapping.md`. Verify each named playbook exists (`tide_list playbooks`). Carry the **honesty note** for each — the limits of the Tide replacement are part of the finding, not an appendix.
103
+
104
+ ### Step 6 — Report
105
+ Emit the report in the format below. Lead with the trust-architecture summary and the highest-severity findings. Put the honest out-of-scope section in the body, not buried.
106
+
107
+ ### Step 7 — Hand off
108
+ Offer the remediation sequence. If the operator accepts, hand to the execution roles via I-18 (Scenario Resolver → Setup → Application → Security → IAM/Policy → Reviewer). Do not begin remediation from this role.
109
+
110
+ ---
111
+
112
+ ## Report Format
113
+
114
+ ```
115
+ # Tide Security Gap Analysis — <system name>
116
+ Scope: <what was inspected> | Authorization: <confirmed by whom>
117
+ Method: static (repo) | both (static + runtime) — list which SG findings were probed
118
+
119
+ ## Trust Architecture
120
+ - Identity: <where credentials live>
121
+ - Authority: <who holds signing keys>
122
+ - Authorization: <where the decision is made>
123
+ - Sensitive data: <what, readable by whom>
124
+
125
+ ## Findings (most severe first)
126
+ ### [SEVERITY] SG-XX — <title>
127
+ - Trust concentration: <the single party/artifact>
128
+ - Evidence: <file:line / config / HTTP obs> [VERIFIED|INFERRED|ASSUMED]
129
+ - Runtime confirmation: <exact request → observed response> [VERIFIED] (omit if static-only or not probed)
130
+ - Failure scenario: <what one compromise yields>
131
+ - Tide replacement: <mechanism> (source: canon/...) [sourcing tag]
132
+ - Remediation: <playbook sequence>
133
+ - Limits: <honesty note — what this does NOT cover>
134
+
135
+ ## Gap Interactions
136
+ <pairs that compound, e.g. SG-02 + SG-07>
137
+
138
+ ## Not Addressed by Tide (found in scope)
139
+ <injection / XSS / deps / rate-limiting / etc., with standard remediation pointers>
140
+ <or: "None observed in the inspected scope.">
141
+
142
+ ## Recommended Remediation Sequence
143
+ 1. <playbook> — closes <SG-XX>
144
+ 2. ...
145
+ Handoff: <execution role to engage first, per I-18>
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Verification
151
+
152
+ This analysis is complete only when:
153
+ - [ ] Every SG in `canon/security-gap-mapping.md` (SG-01 … SG-18) was checked (not sampled), or its non-applicability is stated.
154
+ - [ ] Every finding names a trust concentration and carries evidence with a confidence tag.
155
+ - [ ] Every Tide-replacement claim cites `canon/feature-mapping.md` or `canon/invariants.md`.
156
+ - [ ] Every remediation names a playbook that actually exists.
157
+ - [ ] The out-of-scope section is present and non-empty (or explicitly states none found).
158
+ - [ ] No finding claims Tide fixes an out-of-scope gap class.
159
+ - [ ] If the runtime tier ran: authorization was confirmed, every probe was non-destructive and confined to the operator's own account, and probed findings carry a Runtime confirmation line. Unprobed findings remain at their static confidence.
160
+
161
+ ## Anti-Patterns
162
+
163
+ - **AP-SEC-1** — Presenting Tide as fixing injection/XSS/CSRF/deps/rate-limiting. Kills credibility of the real findings.
164
+ - **AP-SEC-2** — A trust-concentration claim with no evidence artifact. Every finding needs a file, config value, or runtime observation.
165
+ - **AP-SEC-3** — Sampling APIs instead of enumerating them (SG-05). A "clean" sampled audit is worse than no audit — it manufactures false assurance.
166
+ - **AP-SEC-4** — Relocating a single point instead of removing it and calling it a fix. Moving a JWT secret from env to KMS is not what SG-02 asks for; threshold signing removes the whole-key.
167
+ - **AP-SEC-5** — Overstating severity to motivate migration. Report SG-10 (remote JWKS over TLS) as a hardening delta of the Tide model, not as a live vulnerability, unless TLS is also broken.
168
+ - **AP-SEC-6** — Beginning remediation from this role. Analyst reports; execution roles build. Do not blur the boundary.
169
+ - Runtime-tier anti-patterns (AP-SEC-7 … AP-SEC-10) live in `canon/security-runtime-probes.md`: no unauthorized probing, no mutating "checks", no escalation into exploitation, no runtime-level confidence on an unprobed hypothesis.
170
+
171
+ ## Handoff Trace
172
+
173
+ ```
174
+ [TRACE]
175
+ Scenario: security gap analysis of existing (non-Tide) system
176
+ Role: Security Analyst
177
+ Reason: <operator requested audit / migration scoping>
178
+ Preconditions: read-only scope confirmed, authorization confirmed
179
+ Findings: <N gaps, severities>
180
+ Next: <Setup / Scenario Resolver for remediation, or "report only — awaiting operator decision">
181
+ [/TRACE]
182
+ ```
@@ -33,7 +33,7 @@ If multiple paths are plausible and the repo does not resolve it, ask the user b
33
33
  ## When NOT to Trigger
34
34
 
35
35
  - App already has working Tide auth (provider present, adapter JSON loaded, login functional). Proceed to route-and-api-protection or rbac-and-e2ee skill instead.
36
- - User is asking about migrating from Keycloak. Migration is not yet documented (GAP-023). Say so and stop.
36
+ - User is asking about a **direct Keycloak-to-TideCloak** migration. That specific path is not yet documented (GAP-023) say so and stop. (Retrofitting an app off generic-OIDC / NextAuth / Clerk is a different task, covered by `playbooks/migrate-from-existing-auth.md`; the open gap is the KC→TideCloak realm/data migration itself.)
37
37
 
38
38
  ---
39
39
 
@@ -1,195 +0,0 @@
1
- # Server-Side Delegation - Canon
2
-
3
- Canonical reference for server-side admin API calls via delegation tokens.
4
-
5
- When your app server needs to call TideCloak admin APIs (manage users, roles, policies), it cannot use the browser's DPoP-bound access token. The delegation flow issues a new token bound to the server's ephemeral key.
6
-
7
- ---
8
-
9
- ## Packages
10
-
11
- | Package | Side | Purpose |
12
- |---------|------|---------|
13
- | `@tidecloak/server` | Server | `TideDelegation` class, middleware, DPoP proof generation |
14
- | `@tidecloak/js` | Browser | `createTideFetch` wrapper, `signDelegationRequest`, `signDpopApproval` |
15
-
16
- ---
17
-
18
- ## Flow: Forgetful Interrupt Pattern
19
-
20
- 1. Browser calls server endpoint (e.g., `GET /api/admin/users`)
21
- 2. Server `requireDelegation()` middleware checks cache. No cached token.
22
- 3. Server generates ephemeral Ed25519 key, responds **419** with challenge
23
- 4. `createTideFetch` intercepts 419, calls `IAMService.signDelegationRequest(payload)` and `IAMService.signDpopApproval(serverJkt)`
24
- 5. Browser POSTs signed artifacts to `/api/delegation`
25
- 6. Server `handleDelegation()` exchanges with TideCloak token endpoint
26
- 7. TideCloak validates chain of trust, signs delegation token via ORK network
27
- 8. Server caches delegation token, responds `{ delegated: true }`
28
- 9. `createTideFetch` retries original request. Server finds cached token, proceeds.
29
-
30
- Subsequent requests use the cached token until it expires.
31
-
32
- ---
33
-
34
- ## Setup
35
-
36
- ### Server
37
-
38
- ```js
39
- import { TideDelegation } from '@tidecloak/server';
40
-
41
- const delegation = new TideDelegation({
42
- tidecloakUrl: process.env.TIDECLOAK_URL,
43
- realm: process.env.TIDECLOAK_REALM,
44
- clientId: process.env.TIDECLOAK_CLIENT_ID,
45
- });
46
-
47
- // Exchange endpoint
48
- app.post('/api/delegation', authenticate, delegation.handleDelegation());
49
-
50
- // Protected route with delegation
51
- app.get('/api/admin/users',
52
- authenticate,
53
- delegation.requireDelegation(),
54
- async (req, res) => {
55
- const users = await req.delegation.fetch(adminUrl + '/users');
56
- res.json(users);
57
- }
58
- );
59
- ```
60
-
61
- ### Browser
62
-
63
- ```js
64
- import { createTideFetch } from '@tidecloak/js';
65
-
66
- const appFetch = createTideFetch(window.fetch);
67
-
68
- // Delegation is transparent
69
- const users = await appFetch('/api/admin/users');
70
- ```
71
-
72
- ---
73
-
74
- ## Scoped Delegation
75
-
76
- Limit roles in the delegation token. If `roles` is specified, only listed roles/clients appear. Everything else is stripped.
77
-
78
- ```js
79
- // Only read role for my-app client
80
- delegation.requireDelegation({
81
- roles: {
82
- clients: { 'my-app': ['read'] }
83
- }
84
- })
85
-
86
- // Realm + multiple clients
87
- delegation.requireDelegation({
88
- roles: {
89
- realm: ['admin'],
90
- clients: {
91
- 'my-app': ['read', 'write'],
92
- 'other-app': ['view']
93
- }
94
- }
95
- })
96
-
97
- // No filtering (default)
98
- delegation.requireDelegation()
99
- ```
100
-
101
- Rules:
102
- - Omitting `roles` entirely keeps all user roles
103
- - Once `roles` is specified, unlisted realms/clients are stripped
104
- - Cannot escalate. Requesting a role the user does not have silently drops it.
105
-
106
- ---
107
-
108
- ## Token Exchange Endpoint
109
-
110
- The server exchanges artifacts at TideCloak's standard OIDC token endpoint:
111
-
112
- ```
113
- POST /realms/{realm}/protocol/openid-connect/token
114
- ```
115
-
116
- Parameters (form-encoded):
117
- - `grant_type`: `urn:ietf:params:oauth:grant-type:token-exchange`
118
- - `client_id`: app client ID
119
- - `subject_token`: user's access token
120
- - `subject_token_type`: `urn:ietf:params:oauth:token-type:access_token`
121
- - `actor_token`: browser-signed delegation request JWT
122
- - `actor_token_type`: `urn:ietf:params:oauth:token-type:jwt`
123
- - `dpop_approval`: session key approval of server's DPoP key
124
-
125
- Headers:
126
- - `DPoP`: server's DPoP proof JWT (Ed25519, contains server's public key)
127
-
128
- Response: `{ access_token, token_type: "DPoP", expires_in, issued_token_type }`
129
-
130
- ---
131
-
132
- ## Chain of Trust
133
-
134
- Two-hop validation ensures the delegation token is legitimately authorized:
135
-
136
- **HOP 1**: `subject_token.cnf.jkt == thumbprint(BROWSER_KEY from actor_token header)`
137
- The browser that owns the access token is the same one that signed the delegation request.
138
-
139
- **HOP 2**: `actor_token.cnf.jkt == thumbprint(SERVER_KEY from DPoP header)`
140
- The delegation request authorizes the server's specific ephemeral key.
141
-
142
- Validated at two layers:
143
- 1. TideCloak authenticator (Java) - before token is built
144
- 2. ORK signing model (C#) - before token is threshold-signed
145
-
146
- ---
147
-
148
- ## Using req.delegation.fetch()
149
-
150
- The `fetch` helper on `req.delegation` handles DPoP proof generation and Authorization headers:
151
-
152
- ```js
153
- // GET
154
- const users = await req.delegation.fetch(url);
155
-
156
- // POST with JSON
157
- await req.delegation.fetch(url, { method: 'POST', body: { name: 'role' } });
158
-
159
- // PUT
160
- await req.delegation.fetch(url, { method: 'PUT', body: { enabled: true } });
161
-
162
- // DELETE
163
- await req.delegation.fetch(url, { method: 'DELETE' });
164
- ```
165
-
166
- ---
167
-
168
- ## Anti-Patterns
169
-
170
- - **Do not use master admin credentials** for server-side admin calls. Use delegation.
171
- - **Do not forward the user's browser token** to admin API. It is DPoP-bound to the browser's key.
172
- - **Do not skip DPoP on delegation requests**. The delegation token is DPoP-bound to the server's ephemeral key. Every admin API call needs a fresh DPoP proof with `ath` claim.
173
- - **Do not share delegation tokens across users**. They are cached per session ID.
174
- - **Do not hardcode server keys**. They are ephemeral Ed25519 keys, generated per session.
175
-
176
- ---
177
-
178
- ## Prerequisites
179
-
180
- - TideCloak with `tide-chain-of-trust` client authenticator enabled (added by default in recent TideCloak builds)
181
- - `DelegationToken:1` in the VRK model whitelist (requires VRK rotation after adding)
182
- - DPoP enabled on the client
183
- - `@tidecloak/server` installed on the backend
184
- - `createTideFetch` wrapping fetch on the frontend
185
- - An `authenticate` middleware that sets `req.accessToken` from the Authorization header
186
-
187
- ---
188
-
189
- ## Invariants
190
-
191
- - Delegation tokens are short-lived (max 600s / 10 minutes)
192
- - Server key is ephemeral per session, cleared when delegation cache expires
193
- - The browser must approve each server key via ORK enclave (DPoP approval)
194
- - Scoped delegation can only reduce roles, never add them
195
- - Delegation tokens have `act: { sub: clientId }` per RFC 8693
@@ -1,142 +0,0 @@
1
- # Setup Server-Side Delegation
2
-
3
- For apps where the backend needs to call TideCloak admin APIs on behalf of authenticated users.
4
-
5
- ---
6
-
7
- ## When to Use
8
-
9
- - Your server manages users, roles, or policies via TideCloak admin API
10
- - You need server-side role management or access approvals
11
- - You want to avoid using master admin credentials from your server
12
-
13
- ---
14
-
15
- ## Prerequisites
16
-
17
- - TideCloak bootstrapped and running
18
- - `tidecloak.json` exported with `jwk`, `vendorId`, `homeOrkUrl`
19
- - App has working Tide login (provider wired, DPoP enabled)
20
- - Node.js 18+ on the server (Ed25519 support)
21
-
22
- ---
23
-
24
- ## Steps
25
-
26
- ### 1. Install server SDK
27
-
28
- ```bash
29
- npm install @tidecloak/server
30
- ```
31
-
32
- ### 2. Create delegation instance
33
-
34
- ```ts
35
- // server/lib/delegation.ts
36
- import { TideDelegation } from '@tidecloak/server';
37
-
38
- let _delegation: TideDelegation | null = null;
39
-
40
- export function getDelegation(): TideDelegation {
41
- if (!_delegation) {
42
- _delegation = new TideDelegation({
43
- tidecloakUrl: process.env.TIDECLOAK_URL || config['auth-server-url'],
44
- realm: process.env.TIDECLOAK_REALM || config.realm,
45
- clientId: process.env.TIDECLOAK_CLIENT_ID || config.resource,
46
- });
47
- }
48
- return _delegation;
49
- }
50
- ```
51
-
52
- Use a lazy singleton. Do not create a new instance per request.
53
-
54
- ### 3. Wire delegation exchange endpoint
55
-
56
- ```ts
57
- app.post('/api/delegation', authenticate, getDelegation().handleDelegation());
58
- ```
59
-
60
- This endpoint receives signed delegation artifacts from the browser and exchanges them with TideCloak for a delegation token.
61
-
62
- ### 4. Add delegation middleware to admin routes
63
-
64
- ```ts
65
- app.get('/api/admin/users',
66
- authenticate,
67
- requireAdmin,
68
- getDelegation().requireDelegation(),
69
- async (req, res) => {
70
- const adminUrl = `${tidecloakUrl}/admin/realms/${realm}`;
71
- const users = await req.delegation.fetch(`${adminUrl}/users`);
72
- res.json(users);
73
- }
74
- );
75
- ```
76
-
77
- The middleware:
78
- - Checks if a cached delegation token exists for this user session
79
- - If yes: attaches `req.delegation` with `token` and `fetch()` helper
80
- - If no: responds 419 with a delegation challenge
81
-
82
- ### 5. Wrap browser fetch with createTideFetch
83
-
84
- ```ts
85
- import { createTideFetch } from '@tidecloak/js';
86
-
87
- // Wrap the app's fetch function
88
- const appFetch = createTideFetch(window.fetch);
89
-
90
- // Use appFetch for all server calls that may need delegation
91
- const users = await appFetch('/api/admin/users');
92
- ```
93
-
94
- `createTideFetch` intercepts 419 responses, handles the delegation signing flow with the browser's DPoP key, and retries the original request.
95
-
96
- ### 6. Optional: scope delegation roles
97
-
98
- ```ts
99
- // Only include specific roles in the delegation token
100
- getDelegation().requireDelegation({
101
- roles: {
102
- realm: ['admin'],
103
- clients: { 'my-app': ['manage-users'] }
104
- }
105
- })
106
- ```
107
-
108
- If `roles` is specified, only listed roles appear in the delegation token. Unlisted realms/clients are stripped. Cannot add roles the user does not have.
109
-
110
- ---
111
-
112
- ## Verification
113
-
114
- - [ ] `@tidecloak/server` in server package.json
115
- - [ ] `/api/delegation` POST route wired with `handleDelegation()`
116
- - [ ] Admin routes use `requireDelegation()` middleware
117
- - [ ] Browser uses `createTideFetch` wrapper
118
- - [ ] Admin API calls succeed: user list, role management, etc.
119
- - [ ] 419 responses are handled transparently (no user-visible interruption)
120
- - [ ] Delegation token is cached (no 419 on every request)
121
-
122
- ---
123
-
124
- ## Troubleshooting
125
-
126
- **Repeated 419 responses**
127
- Check that `/api/delegation` is wired correctly and the `authenticate` middleware sets `req.accessToken`.
128
-
129
- **"No pending delegation challenge"**
130
- Server restarted between 419 and delegation POST. Browser retries automatically.
131
-
132
- **"Delegation exchange failed"**
133
- Check TideCloak has `tide-chain-of-trust` client authenticator enabled and `DelegationToken:1` in VRK model whitelist. A VRK rotation is needed after adding the model ID.
134
-
135
- **"DPoP is not initialized"**
136
- `signDelegationRequest` needs the DPoP provider. Ensure `useDPoP` is in the TideCloakProvider config object.
137
-
138
- **Delegation token has wrong roles**
139
- If using scoped delegation, everything not listed is excluded. Omit `roles` for full permissions.
140
-
141
- **Admin API returns 403**
142
- The user does not have the required role (e.g., `tide-realm-admin` on `realm-management`). Delegation can only use roles the user already has.