@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,344 @@
|
|
|
1
|
+
# Set Up IGA Admin Panel
|
|
2
|
+
|
|
3
|
+
Build an admin UI for managing IGA change requests with quorum governance.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- Building an admin dashboard that lists, approves, rejects, and commits IGA change requests
|
|
10
|
+
- Need to integrate the TideCloak change-request lifecycle into a custom UI
|
|
11
|
+
- Managing role assignments, group changes, or policy updates through quorum-governed workflows
|
|
12
|
+
|
|
13
|
+
**Do not use** if you only need end-user authentication. See [add-auth-nextjs-fresh.md](add-auth-nextjs-fresh.md) instead.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- TideCloak instance running and accessible
|
|
20
|
+
- TideCloak realm created with IGA enabled
|
|
21
|
+
- Admin user with sufficient privileges (realm-admin or equivalent)
|
|
22
|
+
- Admin bearer token obtainable (via admin-cli client or service account)
|
|
23
|
+
- Familiarity with the Tide enclave signing flow (required for multi-admin mode)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## API Base
|
|
28
|
+
|
|
29
|
+
All endpoints below are relative to:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
{tidecloak-url}/admin/realms/{realm}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Example: `http://localhost:8080/admin/realms/myrealm`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Change Request Lifecycle
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
1. Admin creates change (role assignment, group change, etc.)
|
|
43
|
+
-> Draft created automatically (DRAFT/PENDING)
|
|
44
|
+
2. Admin reviews: GET /tide-admin/change-set/all/requests
|
|
45
|
+
3. Admin approves: POST /tide-admin/change-set/sign/batch
|
|
46
|
+
-> If Tide multi-admin: returns signing challenge for enclave
|
|
47
|
+
-> Submit signed result: POST /tideAdminResources/add-review
|
|
48
|
+
4. Once enough approvals: POST /tide-admin/change-set/commit/batch
|
|
49
|
+
5. Or cancel: POST /tide-admin/change-set/cancel/batch
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Endpoints
|
|
55
|
+
|
|
56
|
+
### Counts (Lightweight)
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
GET /tide-admin/change-set/counts
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use this for dashboard badges and summary views. Avoids fetching full request payloads.
|
|
63
|
+
|
|
64
|
+
### List Requests
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
GET /tide-admin/change-set/all/requests # All change requests (single call)
|
|
68
|
+
GET /tide-admin/change-set/users/requests # User changes only
|
|
69
|
+
GET /tide-admin/change-set/roles/requests # Role changes only
|
|
70
|
+
GET /tide-admin/change-set/clients/requests # Client changes only
|
|
71
|
+
GET /tide-admin/change-set/groups/requests # Group changes only
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Sign / Approve
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
POST /tide-admin/change-set/sign/batch # Approve (sign) changes
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Commit / Cancel
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
POST /tide-admin/change-set/commit/batch # Commit approved changes
|
|
84
|
+
POST /tide-admin/change-set/cancel/batch # Cancel pending changes
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Enclave Review and Rejection
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
POST /tideAdminResources/add-review # Submit Tide enclave signed review (multipart/form-data)
|
|
91
|
+
POST /tideAdminResources/add-rejection # Reject a change (multipart/form-data)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Content-type warning**: Both `add-review` and `add-rejection` use `multipart/form-data`, NOT JSON. All other mutation endpoints (`sign/batch`, `commit/batch`, `cancel/batch`) use `application/json`. If you send JSON to `add-rejection`, the server silently ignores the payload.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Rejection example — must use FormData, not JSON
|
|
98
|
+
const formData = new FormData();
|
|
99
|
+
formData.append("changeSetId", draftRecordId);
|
|
100
|
+
formData.append("actionType", actionType);
|
|
101
|
+
formData.append("changeSetType", changeSetType);
|
|
102
|
+
|
|
103
|
+
await fetch(`${TC_URL}/admin/realms/${realm}/tideAdminResources/add-rejection`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Authorization': `Bearer ${adminToken}` },
|
|
106
|
+
body: formData // NOT JSON.stringify
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Activity and Comments
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
GET /tide-admin/change-set/{id}/activity # Activity log + comments
|
|
114
|
+
POST /tide-admin/change-set/{id}/comments # Add comment to change request
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Approve (Sign) Request -- Code Example
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const response = await fetch(`${TC_URL}/admin/realms/${realm}/tide-admin/change-set/sign/batch`, {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: {
|
|
125
|
+
'Authorization': `Bearer ${adminToken}`,
|
|
126
|
+
'Content-Type': 'application/json'
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
changeSets: [{
|
|
130
|
+
changeSetId: "draft-record-id",
|
|
131
|
+
changeSetType: "USER_ROLE",
|
|
132
|
+
actionType: "CREATE",
|
|
133
|
+
policyRoleId: "optional-role-uuid", // omit to use tide-realm-admin policy
|
|
134
|
+
dynamicData: ["optional-base64"] // custom Forseti contract input
|
|
135
|
+
}]
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// If Tide multi-admin mode:
|
|
140
|
+
// response[0].requiresApprovalPopup === true
|
|
141
|
+
// -> decode changeSetDraftRequests (base64)
|
|
142
|
+
// -> present to Tide enclave for admin signing
|
|
143
|
+
// -> submit via POST /tideAdminResources/add-review
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## TypeScript Client (KcAdminClient) Usage
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import KcAdminClient from "@keycloak/keycloak-admin-client";
|
|
152
|
+
|
|
153
|
+
const client = new KcAdminClient({ baseUrl: "http://localhost:8080" });
|
|
154
|
+
await client.auth({
|
|
155
|
+
username: "admin",
|
|
156
|
+
password: "admin",
|
|
157
|
+
grantType: "password",
|
|
158
|
+
clientId: "admin-cli"
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Counts
|
|
162
|
+
const counts = await client.tideUsersExt.getChangeSetCounts();
|
|
163
|
+
|
|
164
|
+
// All requests
|
|
165
|
+
const all = await client.tideUsersExt.getAllChangeSetRequests();
|
|
166
|
+
|
|
167
|
+
// Approve
|
|
168
|
+
await client.tideUsersExt.approveDraftChangeSet({
|
|
169
|
+
changeSets: [{ changeSetId: "id", changeSetType: "USER_ROLE", actionType: "CREATE" }]
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Commit
|
|
173
|
+
await client.tideUsersExt.commitDraftChangeSet({
|
|
174
|
+
changeSets: [{ changeSetId: "id", changeSetType: "USER_ROLE", actionType: "CREATE" }]
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Activity and comments
|
|
178
|
+
const activity = await client.tideUsersExt.getChangeSetActivity({ id: "draft-id" });
|
|
179
|
+
await client.tideUsersExt.addChangeSetComment({ id: "draft-id", comment: "LGTM" });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Policy Management Endpoints
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
PUT /tide-admin/forseti-contracts # Create/update Forseti contract
|
|
188
|
+
GET /tide-admin/forseti-contracts # List contracts
|
|
189
|
+
POST /tide-admin/policy-templates # Create policy template
|
|
190
|
+
GET /tide-admin/policy-templates # List templates
|
|
191
|
+
PUT /tide-admin/ssh-policies # Attach policy to role
|
|
192
|
+
GET /tide-admin/ssh-policies # List role policies
|
|
193
|
+
POST /tide-iga-provider/role-policy/{roleId}/init-cert # Store signed policy on role (JSON: initCert required, initCertSig optional; no doken field)
|
|
194
|
+
GET /tide-admin/role-policies # Roles with policy status
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## ChangeSetType Values
|
|
200
|
+
|
|
201
|
+
**VERIFIED** (vendor confirmation, GAP-041 resolved). Full enum (14 values):
|
|
202
|
+
|
|
203
|
+
| Value | Description | Draft Status |
|
|
204
|
+
|-------|-------------|-------------|
|
|
205
|
+
| `DEFAULT_ROLES` | Default role assignment | ACTIVE (auto) |
|
|
206
|
+
| `USER` | User creation | DRAFT |
|
|
207
|
+
| `USER_ROLE` | User-to-role assignment | DRAFT |
|
|
208
|
+
| `USER_GROUP_MEMBERSHIP` | User-to-group membership | DRAFT |
|
|
209
|
+
| `ROLE` | Role definition change | ACTIVE (auto, creation) / DRAFT (deletion if users assigned) |
|
|
210
|
+
| `COMPOSITE_ROLE` | Composite role change | DRAFT |
|
|
211
|
+
| `CLIENT` | Client configuration change | DRAFT (when users exist) |
|
|
212
|
+
| `CLIENT_DEFAULT_USER_CONTEXT` | Client scope mappings | DRAFT |
|
|
213
|
+
| `CLIENT_FULLSCOPE` | Client full-scope toggle | DRAFT |
|
|
214
|
+
| `GROUP` | Group definition change | — |
|
|
215
|
+
| `GROUP_ROLE` | Group-to-role assignment | DRAFT |
|
|
216
|
+
| `GROUP_MOVE` | Group hierarchy change | DRAFT |
|
|
217
|
+
| `REALM_LICENSING` | Realm license change | DRAFT |
|
|
218
|
+
| `POLICY` | Policy change (derived from USER_ROLE) | DRAFT |
|
|
219
|
+
|
|
220
|
+
**ACTIVE vs DRAFT**: Role creation and default-role assignment get ACTIVE status (recorded for audit but non-blocking — no sign/commit needed). All other actions get DRAFT status requiring quorum sign/commit. Master realm and IGA-disabled realms are always exempt.
|
|
221
|
+
|
|
222
|
+
## Status Values
|
|
223
|
+
|
|
224
|
+
| Value | Meaning |
|
|
225
|
+
|-------|---------|
|
|
226
|
+
| `DRAFT` | Just created, not yet submitted for review |
|
|
227
|
+
| `PENDING` | Awaiting approvals |
|
|
228
|
+
| `APPROVED` | Enough approvals received, ready to commit |
|
|
229
|
+
| `DENIED` | Rejected by reviewer |
|
|
230
|
+
| `ACTIVE` | Committed and applied |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Admin Panel Requirements
|
|
235
|
+
|
|
236
|
+
A working IGA admin panel must include:
|
|
237
|
+
|
|
238
|
+
1. **Pending list** -- Display all change requests in `DRAFT` or `PENDING` status. Use `GET /tide-admin/change-set/all/requests` and filter by status.
|
|
239
|
+
|
|
240
|
+
2. **Approve button** -- Triggers `POST /tide-admin/change-set/sign/batch` with selected change set IDs.
|
|
241
|
+
|
|
242
|
+
3. **Enclave popup** -- When `requiresApprovalPopup === true` in the sign response, decode `changeSetDraftRequests` (base64) and present the Tide enclave iframe for the admin to complete threshold signing. Submit the signed result via `POST /tideAdminResources/add-review`.
|
|
243
|
+
|
|
244
|
+
4. **Batch support** -- The sign, commit, and cancel endpoints accept arrays. The UI should support selecting multiple change requests and acting on them in a single call.
|
|
245
|
+
|
|
246
|
+
5. **Role check** -- Only users with admin roles should see the panel. Verify the admin token includes the required realm-admin or delegated role before rendering the UI. This is UI gating only; the server enforces authorization on every API call.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Verification Checklist
|
|
251
|
+
|
|
252
|
+
### Lifecycle
|
|
253
|
+
|
|
254
|
+
- [ ] Creating a role assignment produces a DRAFT change request visible via `GET .../all/requests`
|
|
255
|
+
- [ ] Signing a change request transitions status from DRAFT/PENDING toward APPROVED
|
|
256
|
+
- [ ] In multi-admin mode, signing returns `requiresApprovalPopup === true` and enclave flow completes
|
|
257
|
+
- [ ] Committing an APPROVED change request applies the change and sets status to ACTIVE
|
|
258
|
+
- [ ] Cancelling a PENDING change request removes it from the pending list
|
|
259
|
+
|
|
260
|
+
### Endpoints
|
|
261
|
+
|
|
262
|
+
- [ ] `GET .../counts` returns correct totals matching the full request list
|
|
263
|
+
- [ ] `GET .../all/requests` returns all change types (user, role, client, group)
|
|
264
|
+
- [ ] `POST .../sign/batch` accepts an array and processes all items
|
|
265
|
+
- [ ] `POST .../commit/batch` and `POST .../cancel/batch` work with arrays
|
|
266
|
+
- [ ] `POST /tideAdminResources/add-review` accepts the enclave-signed payload
|
|
267
|
+
- [ ] `POST /tideAdminResources/add-rejection` rejects and sets status to DENIED
|
|
268
|
+
- [ ] `GET .../{id}/activity` returns audit trail with timestamps
|
|
269
|
+
- [ ] `POST .../{id}/comments` persists and appears in activity
|
|
270
|
+
|
|
271
|
+
### Authorization
|
|
272
|
+
|
|
273
|
+
- [ ] Unauthenticated requests return 401
|
|
274
|
+
- [ ] Non-admin tokens return 403
|
|
275
|
+
- [ ] Admin tokens with correct roles return 200
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Common Failures
|
|
280
|
+
|
|
281
|
+
### Sign Response Ignored in Multi-Admin Mode
|
|
282
|
+
|
|
283
|
+
**Symptom**: Approve button appears to succeed but change request stays in PENDING.
|
|
284
|
+
|
|
285
|
+
**Cause**: The `requiresApprovalPopup` flag was not checked. In multi-admin mode, signing returns a challenge that must be completed through the Tide enclave.
|
|
286
|
+
|
|
287
|
+
**Fix**: After calling `sign/batch`, check `response[0].requiresApprovalPopup`. If `true`, decode `changeSetDraftRequests`, present the enclave iframe, and submit the signed result via `POST /tideAdminResources/add-review`.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### Committing Before Enough Approvals
|
|
292
|
+
|
|
293
|
+
**Symptom**: `POST .../commit/batch` returns an error or silently fails.
|
|
294
|
+
|
|
295
|
+
**Cause**: The change request has not reached APPROVED status. Quorum threshold not met.
|
|
296
|
+
|
|
297
|
+
**Fix**: Check the status before committing. Only commit change requests with status `APPROVED`. If still `PENDING`, more admin approvals are needed.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### Wrong ChangeSetType in Payload
|
|
302
|
+
|
|
303
|
+
**Symptom**: 400 Bad Request or unexpected behavior when signing/committing.
|
|
304
|
+
|
|
305
|
+
**Cause**: The `changeSetType` in the request body does not match the actual type of the draft record.
|
|
306
|
+
|
|
307
|
+
**Fix**: Read the `changeSetType` from the `GET .../all/requests` response and pass it back exactly. Do not hardcode a type.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### Missing Authorization Header
|
|
312
|
+
|
|
313
|
+
**Symptom**: 401 on all admin API calls.
|
|
314
|
+
|
|
315
|
+
**Cause**: Admin token not included or expired.
|
|
316
|
+
|
|
317
|
+
**Fix**: Obtain a fresh admin token before each batch of calls. If using `KcAdminClient`, call `client.auth(...)` and handle token refresh.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### Rejection Silently Fails
|
|
322
|
+
|
|
323
|
+
**Symptom**: Clicking "Reject" appears to succeed, but the change request stays in PENDING. No error returned.
|
|
324
|
+
|
|
325
|
+
**Cause**: The rejection endpoint (`POST /tideAdminResources/add-rejection`) expects `multipart/form-data`. If you send `Content-Type: application/json`, the server accepts the request but ignores the payload fields.
|
|
326
|
+
|
|
327
|
+
**Fix**: Use `FormData`, not `JSON.stringify`. Do not set `Content-Type` manually — the browser sets the correct multipart boundary automatically. See the code example in the Endpoints section above.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
### Counts Out of Sync with List
|
|
332
|
+
|
|
333
|
+
**Symptom**: Dashboard badge shows a different number than the actual list.
|
|
334
|
+
|
|
335
|
+
**Cause**: `GET .../counts` and `GET .../all/requests` called at different times; a change was created or committed between calls.
|
|
336
|
+
|
|
337
|
+
**Fix**: Refresh counts after any mutation (sign, commit, cancel). Do not cache counts across operations.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## References
|
|
342
|
+
|
|
343
|
+
- Source: `sources/docs/CHANGE_REQUEST_API.md` (normative), operational exemplars (keylessh, forseti-crypto-quickstart, tidecloak-test-cases)
|
|
344
|
+
- Content-type asymmetry confirmed by: CHANGE_REQUEST_API.md, forseti-crypto-quickstart `tidecloakApi.ts` (uses FormData for `add-review`)
|
|
@@ -0,0 +1,142 @@
|
|
|
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.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Start TideCloak for Development
|
|
2
|
+
|
|
3
|
+
Start a TideCloak container for local development. This produces a running server — no realm, no client, no admin user yet.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- First step of any TideCloak bootstrap
|
|
10
|
+
- Rebuilding a local TideCloak after a wipe
|
|
11
|
+
- Need a clean TideCloak for testing
|
|
12
|
+
|
|
13
|
+
**Do not use** if TideCloak is already running and healthy. Check: `curl -sf http://localhost:8080 > /dev/null && echo "Running"`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Docker installed and running (`docker info` succeeds)
|
|
20
|
+
- `sudo` access (data directory permissions)
|
|
21
|
+
- Port 8080 available (or adjust mapping)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Steps
|
|
26
|
+
|
|
27
|
+
### Step 1: Choose image
|
|
28
|
+
|
|
29
|
+
| Need | Image |
|
|
30
|
+
|------|-------|
|
|
31
|
+
| Standard development and production | `tideorg/tidecloak-dev:latest` |
|
|
32
|
+
| Staging / testing pre-release features | `tideorg/tidecloak-stg-dev:latest` |
|
|
33
|
+
|
|
34
|
+
Both images include the full Tide protocol. `tidecloak-dev` is the production image. `tidecloak-stg-dev` is the development/staging image for testing.
|
|
35
|
+
|
|
36
|
+
**Do not append `start-dev` or any command** to `docker run`. TideCloak images have a pre-configured entrypoint. Appending `start-dev` (a vanilla Keycloak convention) breaks Tide initialization.
|
|
37
|
+
|
|
38
|
+
### Step 2: Clean previous state
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
docker stop tidecloak 2>/dev/null; docker rm tidecloak 2>/dev/null
|
|
42
|
+
mkdir -p ./data
|
|
43
|
+
sudo rm -f ./data/keycloakdb* 2>/dev/null
|
|
44
|
+
sudo chown -R 1000:1000 ./data
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Check port conflict:
|
|
48
|
+
```bash
|
|
49
|
+
lsof -i :8080 >/dev/null 2>&1 && echo "ERROR: Port 8080 in use" && exit 1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 3: Start container
|
|
53
|
+
|
|
54
|
+
**Dev image (recommended for development):**
|
|
55
|
+
|
|
56
|
+
No ORK/threshold env vars needed — the dev image has built-in defaults.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
sudo docker run -d --name tidecloak \
|
|
60
|
+
-v "$(pwd)/data:/opt/keycloak/data/h2" \
|
|
61
|
+
-p 8080:8080 \
|
|
62
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
63
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
|
|
64
|
+
tideorg/tidecloak-dev:latest
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Staging image (pre-release testing only):**
|
|
68
|
+
|
|
69
|
+
Requires ORK, threshold, and payer config.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
sudo docker run -d --name tidecloak \
|
|
73
|
+
-v "$(pwd)/data:/opt/keycloak/data/h2" \
|
|
74
|
+
-p 8080:8080 \
|
|
75
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
76
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
|
|
77
|
+
-e KC_HOSTNAME="${TIDECLOAK_URL:-http://localhost:8080}" \
|
|
78
|
+
-e SYSTEM_HOME_ORK=https://sork1.tideprotocol.com \
|
|
79
|
+
-e USER_HOME_ORK=https://sork1.tideprotocol.com \
|
|
80
|
+
-e THRESHOLD_T=3 \
|
|
81
|
+
-e THRESHOLD_N=5 \
|
|
82
|
+
-e PAYER_PUBLIC=20000011d6a0e8212d682657147d864b82d10e92776c15ead43dcfdc100ebf4dcfe6a8 \
|
|
83
|
+
tideorg/tidecloak-stg-dev:latest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 4: Wait for readiness
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
for i in {1..15}; do
|
|
90
|
+
curl -sf http://localhost:8080 > /dev/null 2>&1 && echo "TideCloak ready" && break
|
|
91
|
+
echo "Waiting (attempt $i/15)..."
|
|
92
|
+
sleep 5
|
|
93
|
+
done
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
TideCloak typically takes 30–60 seconds to start. If it does not respond after 15 attempts, check `docker logs tidecloak`.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Verification
|
|
101
|
+
|
|
102
|
+
- [ ] `docker ps` shows `tidecloak` running
|
|
103
|
+
- [ ] `curl -sf http://localhost:8080` returns 200
|
|
104
|
+
- [ ] `docker logs tidecloak` shows no fatal errors
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Common Failures
|
|
109
|
+
|
|
110
|
+
| Symptom | Cause | Fix |
|
|
111
|
+
|---------|-------|-----|
|
|
112
|
+
| Container exits immediately | Stale H2 DB files | `sudo rm -f ./data/keycloakdb*` and restart |
|
|
113
|
+
| `AccessDeniedException` in logs | Wrong data directory mount or permissions | Mount `./data` not `.`, run `sudo chown -R 1000:1000 ./data` |
|
|
114
|
+
| Port already in use | Another container or service on 8080 | `docker rm -f $(docker ps -q --filter publish=8080)` |
|
|
115
|
+
| Exit code 2, HTTPS warnings | Named volume from previous docker-compose | `docker volume rm <volume_name>`, use fresh `./data` |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Anti-Patterns
|
|
120
|
+
|
|
121
|
+
- **Do not** mount project root as data volume. Use `./data` subdirectory.
|
|
122
|
+
- **Do not** omit Tide env vars (`SYSTEM_HOME_ORK`, `USER_HOME_ORK`, etc.) when using the staging image. The dev image has built-in defaults and does not need them.
|
|
123
|
+
- **Do not** use `THRESHOLD_T=1` anywhere. Single-ORK compromise.
|
|
124
|
+
- **Do not** reuse data directory across environments. Clean between setups.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Next Step
|
|
129
|
+
|
|
130
|
+
Proceed to playbook `bootstrap-realm-from-template`.
|