@tideorg/mcp 1.4.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/GAP_REGISTER.md +117 -0
- package/README.md +38 -0
- package/adapters/AGENTS.md +241 -0
- package/adapters/CLAUDE.md +146 -0
- package/adapters/replit.md +224 -0
- package/canon/anti-patterns.md +2133 -0
- package/canon/concepts.md +816 -0
- package/canon/custom-contracts.md +533 -0
- package/canon/delegation.md +195 -0
- package/canon/feature-mapping.md +637 -0
- package/canon/framework-matrix.md +1125 -0
- package/canon/invariants.md +851 -0
- package/canon/redirect-handler.md +254 -0
- package/canon/tidecloak-bootstrap.md +287 -0
- package/canon/tidecloak-endpoints.md +294 -0
- package/canon/troubleshooting.md +1494 -0
- package/canon/version-policy.md +89 -0
- package/mcp-server/dist/index.d.ts +2 -0
- package/mcp-server/dist/index.js +605 -0
- package/package.json +45 -0
- package/playbooks/add-auth-nextjs-existing.md +799 -0
- package/playbooks/add-auth-nextjs-fresh.md +654 -0
- package/playbooks/add-rbac-nextjs.md +190 -0
- package/playbooks/bootstrap-realm-from-template.md +170 -0
- package/playbooks/configure-e2ee-roles-and-policies.md +196 -0
- package/playbooks/deploy-tidecloak-docker.md +792 -0
- package/playbooks/diagnose-broken-login.md +234 -0
- package/playbooks/diagnose-missing-roles-or-claims.md +253 -0
- package/playbooks/initialize-admin-and-link-account.md +235 -0
- package/playbooks/migrate-from-existing-auth.md +198 -0
- package/playbooks/protect-api-nextjs.md +451 -0
- package/playbooks/protect-routes-nextjs.md +544 -0
- package/playbooks/setup-forseti-e2ee.md +756 -0
- package/playbooks/setup-iga-admin-panel.md +344 -0
- package/playbooks/setup-server-delegation.md +142 -0
- package/playbooks/start-tidecloak-dev.md +130 -0
- package/playbooks/verify-jwt-server-side.md +439 -0
- package/prompts/add-admin-approval-flow.md +50 -0
- package/prompts/build-private-customer-portal.md +85 -0
- package/prompts/migrate-generic-auth-to-tide.md +67 -0
- package/prompts/secure-existing-app.md +80 -0
- package/reference-apps/INDEX.md +87 -0
- package/reference-apps/encrypted-communication/anti-patterns.md +123 -0
- package/reference-apps/encrypted-communication/bootstrap-sequence.md +152 -0
- package/reference-apps/encrypted-communication/manifest.yaml +100 -0
- package/reference-apps/encrypted-communication/role-policy-matrix.md +80 -0
- package/reference-apps/encrypted-communication/scenario.md +134 -0
- package/reference-apps/git-pr-signing-service/anti-patterns.md +61 -0
- package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +157 -0
- package/reference-apps/git-pr-signing-service/manifest.yaml +80 -0
- package/reference-apps/git-pr-signing-service/role-policy-matrix.md +99 -0
- package/reference-apps/git-pr-signing-service/scenario.md +169 -0
- package/reference-apps/iga-admin-governance/anti-patterns.md +133 -0
- package/reference-apps/iga-admin-governance/bootstrap-sequence.md +153 -0
- package/reference-apps/iga-admin-governance/manifest.yaml +87 -0
- package/reference-apps/iga-admin-governance/role-policy-matrix.md +67 -0
- package/reference-apps/iga-admin-governance/scenario.md +126 -0
- package/reference-apps/organisation-password-manager/anti-patterns.md +71 -0
- package/reference-apps/organisation-password-manager/bootstrap-sequence.md +127 -0
- package/reference-apps/organisation-password-manager/manifest.yaml +86 -0
- package/reference-apps/organisation-password-manager/role-policy-matrix.md +59 -0
- package/reference-apps/organisation-password-manager/scenario.md +107 -0
- package/reference-apps/policy-governed-signing/anti-patterns.md +67 -0
- package/reference-apps/policy-governed-signing/bootstrap-sequence.md +128 -0
- package/reference-apps/policy-governed-signing/manifest.yaml +78 -0
- package/reference-apps/policy-governed-signing/role-policy-matrix.md +62 -0
- package/reference-apps/policy-governed-signing/scenario.md +113 -0
- package/skills/tide-diagnostics/SKILL.md +99 -0
- package/skills/tide-integration/SKILL.md +136 -0
- package/skills/tide-learning-capture/SKILL.md +174 -0
- package/skills/tide-rbac-and-e2ee/SKILL.md +214 -0
- package/skills/tide-reviewer/SKILL.md +133 -0
- package/skills/tide-route-and-api-protection/SKILL.md +201 -0
- package/skills/tide-scenario-resolver/SKILL.md +81 -0
- package/skills/tide-setup/SKILL.md +218 -0
- package/skills/tide-solutions-architect/SKILL.md +130 -0
|
@@ -0,0 +1,195 @@
|
|
|
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
|