@tideorg/mcp 1.4.2 → 1.9.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/CHANGELOG.md +81 -0
- package/GAP_REGISTER.md +18 -8
- package/PRIVACY.md +41 -0
- package/README.md +204 -197
- package/adapters/AGENTS.md +3 -2
- package/adapters/CLAUDE.md +1 -1
- package/canon/anti-patterns.md +55 -62
- package/canon/concepts.md +46 -34
- package/canon/custom-contracts.md +12 -8
- package/canon/feature-mapping.md +27 -75
- package/canon/framework-matrix.md +69 -22
- package/canon/hosting-options.md +111 -0
- package/canon/iga-change-requests-api.md +211 -0
- package/canon/invariants.md +33 -35
- package/canon/security-gap-mapping.md +506 -0
- package/canon/security-runtime-probes.md +177 -0
- package/canon/tidecloak-bootstrap.md +21 -16
- package/canon/tidecloak-endpoints.md +60 -98
- package/canon/troubleshooting.md +115 -53
- package/canon/version-policy.md +8 -12
- package/mcp-server/dist/server.js +221 -19
- package/package.json +48 -45
- package/playbooks/add-auth-nextjs-existing.md +33 -17
- package/playbooks/add-auth-nextjs-fresh.md +1 -1
- package/playbooks/add-rbac-nextjs.md +7 -2
- package/playbooks/bootstrap-realm-from-template.md +33 -18
- package/playbooks/deploy-tidecloak-docker.md +31 -28
- package/playbooks/diagnose-missing-roles-or-claims.md +2 -2
- package/playbooks/initialize-admin-and-link-account.md +22 -19
- package/playbooks/protect-api-nextjs.md +40 -1
- package/playbooks/protect-aspnet-core-asgard.md +313 -0
- package/playbooks/protect-routes-nextjs.md +24 -10
- package/playbooks/provision-tidecloak-skycloak.md +213 -0
- package/playbooks/setup-forseti-e2ee.md +32 -50
- package/playbooks/setup-iga-admin-panel.md +113 -171
- package/playbooks/verify-jwt-server-side.md +20 -1
- package/prompts/build-private-customer-portal.md +1 -1
- package/prompts/security-gap-analysis.md +55 -0
- package/reference-apps/encrypted-communication/anti-patterns.md +3 -3
- package/reference-apps/encrypted-communication/bootstrap-sequence.md +2 -2
- package/reference-apps/encrypted-communication/scenario.md +2 -2
- package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +8 -8
- package/reference-apps/iga-admin-governance/anti-patterns.md +18 -16
- package/reference-apps/iga-admin-governance/bootstrap-sequence.md +10 -5
- package/reference-apps/iga-admin-governance/role-policy-matrix.md +12 -13
- package/reference-apps/iga-admin-governance/scenario.md +15 -13
- package/reference-apps/organisation-password-manager/bootstrap-sequence.md +5 -6
- package/reference-apps/policy-governed-signing/bootstrap-sequence.md +9 -9
- package/skills/tide-diagnostics/SKILL.md +1 -1
- package/skills/tide-integration/SKILL.md +9 -18
- package/skills/tide-mcp-qa/SKILL.md +139 -0
- package/skills/tide-rbac-and-e2ee/SKILL.md +5 -5
- package/skills/tide-reviewer/SKILL.md +1 -1
- package/skills/tide-security-analyst/SKILL.md +182 -0
- package/skills/tide-setup/SKILL.md +1 -1
- package/canon/delegation.md +0 -195
- package/playbooks/setup-server-delegation.md +0 -142
|
@@ -12,6 +12,13 @@ Build an admin UI for managing IGA change requests with quorum governance.
|
|
|
12
12
|
|
|
13
13
|
**Do not use** if you only need end-user authentication. See [add-auth-nextjs-fresh.md](add-auth-nextjs-fresh.md) instead.
|
|
14
14
|
|
|
15
|
+
**A custom panel is optional.** The stack already ships two working admin consoles that manage IGA change requests out of the box:
|
|
16
|
+
|
|
17
|
+
- **tide-console SPA** — a built Vite/React admin UI bundled inside the `tidecloak-key-provider` jar and served by TideCloak.
|
|
18
|
+
- **Legacy keycloak.v2 console** — the stock Keycloak admin UI, which on the shipped build carries a native `tide-change-requests` section.
|
|
19
|
+
|
|
20
|
+
Build a custom panel only when you need to embed CR governance inside your own product UI. Otherwise use one of the shipped consoles.
|
|
21
|
+
|
|
15
22
|
---
|
|
16
23
|
|
|
17
24
|
## Prerequisites
|
|
@@ -34,190 +41,136 @@ All endpoints below are relative to:
|
|
|
34
41
|
|
|
35
42
|
Example: `http://localhost:8080/admin/realms/myrealm`
|
|
36
43
|
|
|
44
|
+
The consolidated IGA REST surface is rooted at `/admin/realms/{realm}/iga` (server class `IgaAdminResource`). Change requests live under `/iga/change-requests`.
|
|
45
|
+
|
|
46
|
+
> **Legacy bootstrap-compat surface.** The canonical bootstrap script (`localtest/init/init_tidecloak.sh`) and the `tidecloak-js` `AdminAPI` still call an older `/tide-admin/change-set/{type}/requests`, `/tide-admin/change-set/sign`, and `/tide-admin/change-set/commit` surface. That path still exists for backward compatibility and is fine for scripted bootstrap approval, but the `/iga/*` surface documented below is the current API for a live admin panel. Do not mix the two.
|
|
47
|
+
|
|
37
48
|
---
|
|
38
49
|
|
|
39
50
|
## Change Request Lifecycle
|
|
40
51
|
|
|
52
|
+
Current `/iga/change-requests/...` surface (replaces legacy `/tide-admin/change-set/...`, confirmed by Tide 2026-07-07). **Full spec: `canon/iga-change-requests-api.md`.**
|
|
53
|
+
|
|
41
54
|
```
|
|
42
|
-
1. Admin
|
|
43
|
-
->
|
|
44
|
-
2. Admin reviews:
|
|
45
|
-
3. Admin approves: POST /
|
|
46
|
-
->
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
1. Admin performs a governed change (role assignment, group change, etc.)
|
|
56
|
+
-> The write returns 202 Accepted; a change request is created (PENDING)
|
|
57
|
+
2. Admin reviews: GET /iga/change-requests?status=PENDING
|
|
58
|
+
3. Admin approves: POST /iga/change-requests/{id}/authorize (body {} optional)
|
|
59
|
+
-> Tide MultiAdmin mode: sign via the enclave two-phase exchange
|
|
60
|
+
GET/POST /iga/change-requests/{id}/approval-model
|
|
61
|
+
-> FirstAdmin / Tideless mode: authorize signs server-side, no popup
|
|
62
|
+
4. Once threshold met: POST /iga/change-requests/{id}/commit (412 if under threshold)
|
|
63
|
+
5. Or reject: POST /iga/change-requests/{id}/deny
|
|
50
64
|
```
|
|
51
65
|
|
|
66
|
+
`authorize` (simple lane: firstAdmin/Tideless) records an approval and **never commits** — it refuses tide-multiAdmin CRs. `approve` is the multiAdmin enclave lane: it approves AND auto-commits once quorum is met (this is the deployed console "Authorize" button). `commit` is the explicit apply-only step for a CR that already has enough approvals (412 if sub-quorum). Use `bulk-authorize` to act on many at once.
|
|
67
|
+
|
|
52
68
|
---
|
|
53
69
|
|
|
54
70
|
## Endpoints
|
|
55
71
|
|
|
56
|
-
###
|
|
72
|
+
### List / Get
|
|
57
73
|
|
|
58
74
|
```
|
|
59
|
-
GET /
|
|
75
|
+
GET /iga/change-requests?status=PENDING # PENDING|APPROVED|DENIED|CANCELLED
|
|
76
|
+
GET /iga/change-requests/{id} # single CR (keyed by `id`)
|
|
60
77
|
```
|
|
61
78
|
|
|
62
|
-
|
|
79
|
+
Each CR carries `id`, `entityType`, `actionType`, `status`, `readyToCommit`, `threshold`, `authorizers[]`, `dependsOn`/`blocked`. Derive dashboard counts from the list (there is no separate counts endpoint on the new surface; no per-type request endpoints and no `all/requests` path either).
|
|
63
80
|
|
|
64
|
-
###
|
|
81
|
+
### Authorize (Sign)
|
|
65
82
|
|
|
66
83
|
```
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
84
|
+
POST /iga/change-requests/{id}/authorize # body {} optional → 200/403/409 (409 = four-eyes)
|
|
85
|
+
POST /iga/change-requests/bulk-authorize # {"actionTypeIn":["CREATE","DELETE"],"limit":100} → 429 if a bulk run is active
|
|
72
86
|
```
|
|
73
87
|
|
|
74
|
-
###
|
|
88
|
+
### Commit / Deny
|
|
75
89
|
|
|
76
90
|
```
|
|
77
|
-
POST /
|
|
91
|
+
POST /iga/change-requests/{id}/commit # → 200 (APPROVED) / 412 (under threshold or unmet dependency)
|
|
92
|
+
POST /iga/change-requests/{id}/deny # → 204
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
###
|
|
95
|
+
### Enclave Review (Tide MultiAdmin)
|
|
81
96
|
|
|
82
97
|
```
|
|
83
|
-
|
|
84
|
-
POST /
|
|
98
|
+
GET /iga/change-requests/{id}/approval-model # → { changeRequestId, requestModel(base64) }
|
|
99
|
+
POST /iga/change-requests/{id}/approval-model # { "requestModel": "<base64 doken>" } → { recorded, authCount, threshold }
|
|
85
100
|
```
|
|
86
101
|
|
|
87
|
-
|
|
102
|
+
The two-phase `approval-model` exchange replaces the legacy `tideAdminResources/add-review`. In FirstAdmin / Tideless mode, `authorize` signs server-side and no enclave step is needed.
|
|
103
|
+
|
|
104
|
+
### Deprecated: add-review / add-rejection
|
|
88
105
|
|
|
89
106
|
```
|
|
90
|
-
POST /tideAdminResources/add-review #
|
|
91
|
-
POST /tideAdminResources/add-rejection #
|
|
107
|
+
POST /tideAdminResources/add-review # 410 Gone (deprecated)
|
|
108
|
+
POST /tideAdminResources/add-rejection # 410 Gone (deprecated)
|
|
92
109
|
```
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
Both endpoints were removed as part of the IGA decoupling and now return **`410 Gone`**. Approval goes through `/iga/change-requests/{id}/authorize` (plus the `approval-model` enclave exchange in Tide MultiAdmin mode) and rejection through `/iga/change-requests/{id}/deny`.
|
|
95
112
|
|
|
96
|
-
|
|
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
|
|
113
|
+
### Comments
|
|
111
114
|
|
|
112
115
|
```
|
|
113
|
-
GET
|
|
114
|
-
|
|
116
|
+
GET/POST /iga/change-requests/{id}/comments # { "comment": "..." } (≤2000 chars)
|
|
117
|
+
PUT/DELETE /iga/change-requests/{id}/comments/{commentId}
|
|
115
118
|
```
|
|
116
119
|
|
|
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
|
-
```
|
|
120
|
+
There is no per-request `/activity` endpoint on the current surface; comments are the audit thread.
|
|
145
121
|
|
|
146
122
|
---
|
|
147
123
|
|
|
148
|
-
## TypeScript Client
|
|
124
|
+
## TypeScript Client Usage
|
|
125
|
+
|
|
126
|
+
There is no `@keycloak/keycloak-admin-client` `KcAdminClient.tideUsersExt` extension on `tidecloak-js` main. The change-request surface is a hand-rolled fetch client, `AdminAPI` (`packages/tidecloak-js/src/AdminAPI.js`), exposed through `@tidecloak/react`. It targets the legacy bootstrap-compat `/tide-admin/change-set/*` endpoints:
|
|
149
127
|
|
|
150
128
|
```typescript
|
|
151
|
-
import
|
|
129
|
+
import { AdminAPI } from "@tidecloak/react";
|
|
152
130
|
|
|
153
|
-
const
|
|
154
|
-
await client.auth({
|
|
155
|
-
username: "admin",
|
|
156
|
-
password: "admin",
|
|
157
|
-
grantType: "password",
|
|
158
|
-
clientId: "admin-cli"
|
|
159
|
-
});
|
|
131
|
+
const api = new AdminAPI({ baseUrl, realm, token });
|
|
160
132
|
|
|
161
|
-
//
|
|
162
|
-
const
|
|
133
|
+
// List (combines user + role change requests)
|
|
134
|
+
const pending = await api.getPendingChangeSets();
|
|
135
|
+
const userCrs = await api.getUserChangeRequests();
|
|
136
|
+
const roleCrs = await api.getRoleChangeRequests();
|
|
163
137
|
|
|
164
|
-
//
|
|
165
|
-
|
|
138
|
+
// Approve a single change set (sent as FormData, single object — not { changeSets: [...] })
|
|
139
|
+
await api.approveChangeSet({ changeSetId, actionType, changeSetType });
|
|
166
140
|
|
|
167
|
-
//
|
|
168
|
-
await
|
|
169
|
-
changeSets: [{ changeSetId: "id", changeSetType: "USER_ROLE", actionType: "CREATE" }]
|
|
170
|
-
});
|
|
141
|
+
// Multi-admin: approve with an enclave signature
|
|
142
|
+
await api.approveChangeSetWithSignature(/* signed payload */);
|
|
171
143
|
|
|
172
144
|
// Commit
|
|
173
|
-
await
|
|
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" });
|
|
145
|
+
await api.commitChangeSet({ changeSetId, actionType, changeSetType });
|
|
180
146
|
```
|
|
181
147
|
|
|
148
|
+
There are no `getChangeSetCounts`, `getAllChangeSetRequests`, `getChangeSetActivity`, or `addChangeSetComment` methods. For a panel built on the current `/iga/*` surface, call the `/iga/change-requests/*` endpoints (see the **Endpoints** section above and `canon/iga-change-requests-api.md`) directly with `fetch` rather than via this SDK.
|
|
149
|
+
|
|
182
150
|
---
|
|
183
151
|
|
|
184
152
|
## Policy Management Endpoints
|
|
185
153
|
|
|
186
154
|
```
|
|
187
|
-
|
|
188
|
-
GET /
|
|
189
|
-
POST /
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
155
|
+
GET /iga/forseti-contracts # List contracts
|
|
156
|
+
GET /iga/forseti-contracts/{id} # Get one contract
|
|
157
|
+
POST /iga/forseti-contracts # Create a Forseti contract (POST, not PUT)
|
|
158
|
+
DELETE /iga/forseti-contracts/{id} # Delete a contract
|
|
159
|
+
GET /iga/role-policies # Roles with policy status
|
|
195
160
|
```
|
|
196
161
|
|
|
162
|
+
Create is `POST /iga/forseti-contracts` (there is no PUT). Policy-template, SSH-policy, and `role-policy/{roleId}/init-cert` endpoints are not part of the current surface.
|
|
163
|
+
|
|
197
164
|
---
|
|
198
165
|
|
|
199
|
-
##
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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.
|
|
166
|
+
## Change Request Taxonomy
|
|
167
|
+
|
|
168
|
+
The current surface does not use a `ChangeSetType` enum. Each change request carries two free-form String columns on `IgaChangeRequestEntity`:
|
|
169
|
+
|
|
170
|
+
- **`entityType`** — one of: `USER`, `REALM`, `ROLE`, `GROUP`, `CLIENT`, `CLIENT_SCOPE`, `ORGANIZATION`, `COMPOSITE_ROLE`, `CLIENT_SCOPE_CLIENT`, `CLIENT_SCOPE_ROLE`, `PROTOCOL_MAPPER`, `REALM_DEFAULT_SCOPE`, `SCOPE_MAPPING`.
|
|
171
|
+
- **`actionType`** — a free-form verb such as `CREATE`, `SET`, `UPDATE`, `ASSIGN`, or an `ADOPT_*` variant.
|
|
172
|
+
|
|
173
|
+
Read both values back from the `GET /iga/change-requests` response; do not hardcode a fixed enum. Some change requests (e.g. role creation and default-role adoption) commit automatically as part of the login-closure converge and never need an explicit authorize/commit; the rest require quorum authorize/commit. The master realm and IGA-disabled realms are exempt from governance entirely.
|
|
221
174
|
|
|
222
175
|
## Status Values
|
|
223
176
|
|
|
@@ -235,13 +188,13 @@ GET /tide-admin/role-policies # Roles with policy status
|
|
|
235
188
|
|
|
236
189
|
A working IGA admin panel must include:
|
|
237
190
|
|
|
238
|
-
1. **Pending list** -- Display
|
|
191
|
+
1. **Pending list** -- Display change requests via `GET /iga/change-requests?status=PENDING` (keyed by `id`).
|
|
239
192
|
|
|
240
|
-
2. **Approve button** -- Triggers `POST /
|
|
193
|
+
2. **Approve button** -- Triggers `POST /iga/change-requests/{id}/authorize` per selected CR (or `POST /iga/change-requests/bulk-authorize` for a batch).
|
|
241
194
|
|
|
242
|
-
3. **Enclave popup** --
|
|
195
|
+
3. **Enclave popup** -- In Tide MultiAdmin mode, complete the two-phase enclave exchange: `GET /iga/change-requests/{id}/approval-model` → sign in the enclave → `POST /iga/change-requests/{id}/approval-model` with the base64 doken. In FirstAdmin/Tideless mode, `authorize` signs server-side and no popup is needed.
|
|
243
196
|
|
|
244
|
-
4. **Batch support** --
|
|
197
|
+
4. **Batch support** -- Use `POST /iga/change-requests/bulk-authorize` (`{"actionTypeIn":[...],"limit":N}`) to approve many CRs at once; commit each ready CR (`readyToCommit === true`).
|
|
245
198
|
|
|
246
199
|
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
200
|
|
|
@@ -251,22 +204,21 @@ A working IGA admin panel must include:
|
|
|
251
204
|
|
|
252
205
|
### Lifecycle
|
|
253
206
|
|
|
254
|
-
- [ ]
|
|
255
|
-
- [ ]
|
|
256
|
-
- [ ] In
|
|
257
|
-
- [ ] Committing
|
|
258
|
-
- [ ]
|
|
207
|
+
- [ ] A governed write returns 202 and a change request appears via `GET /iga/change-requests?status=PENDING`
|
|
208
|
+
- [ ] `POST .../{id}/authorize` records an approval (409 if the same admin re-signs)
|
|
209
|
+
- [ ] In Tide MultiAdmin mode, the `approval-model` enclave exchange completes and increments `authCount`
|
|
210
|
+
- [ ] Committing a ready CR (`readyToCommit`) applies the change (APPROVED); 412 while under threshold
|
|
211
|
+
- [ ] `POST .../{id}/deny` removes it from the pending list (DENIED)
|
|
259
212
|
|
|
260
213
|
### Endpoints
|
|
261
214
|
|
|
262
|
-
- [ ] `GET
|
|
263
|
-
- [ ] `
|
|
264
|
-
- [ ] `POST
|
|
265
|
-
- [ ] `POST
|
|
266
|
-
- [ ] `POST /
|
|
267
|
-
- [ ] `POST /
|
|
268
|
-
- [ ] `
|
|
269
|
-
- [ ] `POST .../{id}/comments` persists and appears in activity
|
|
215
|
+
- [ ] `GET /iga/change-requests?status=PENDING` returns all pending CRs across types
|
|
216
|
+
- [ ] `POST /iga/change-requests/{id}/authorize` records the approval
|
|
217
|
+
- [ ] `POST /iga/change-requests/bulk-authorize` authorizes a batch (429 if a bulk run is active)
|
|
218
|
+
- [ ] `POST /iga/change-requests/{id}/commit` applies the change; 412 under threshold
|
|
219
|
+
- [ ] `POST /iga/change-requests/{id}/deny` sets status to DENIED
|
|
220
|
+
- [ ] `GET/POST /iga/change-requests/{id}/comments` persist and read back
|
|
221
|
+
- [ ] `POST /tideAdminResources/add-review` and `/add-rejection` return `410 Gone` (deprecated — do not use)
|
|
270
222
|
|
|
271
223
|
### Authorization
|
|
272
224
|
|
|
@@ -278,33 +230,33 @@ A working IGA admin panel must include:
|
|
|
278
230
|
|
|
279
231
|
## Common Failures
|
|
280
232
|
|
|
281
|
-
###
|
|
233
|
+
### Enclave Step Skipped in Tide MultiAdmin Mode
|
|
282
234
|
|
|
283
|
-
**Symptom**: Approve
|
|
235
|
+
**Symptom**: Approve appears to succeed but the change request stays in PENDING and `authCount` does not increase.
|
|
284
236
|
|
|
285
|
-
**Cause**:
|
|
237
|
+
**Cause**: In Tide MultiAdmin mode, a bare `authorize` is not enough — the admin must sign the enclave challenge via the two-phase `approval-model` exchange.
|
|
286
238
|
|
|
287
|
-
**Fix**:
|
|
239
|
+
**Fix**: `GET /iga/change-requests/{id}/approval-model`, decode `requestModel`, sign it in the Tide enclave, then `POST /iga/change-requests/{id}/approval-model` with `{ "requestModel": "<base64 doken>" }`. (FirstAdmin/Tideless mode signs server-side on `authorize` — no enclave step.)
|
|
288
240
|
|
|
289
241
|
---
|
|
290
242
|
|
|
291
|
-
### Committing Before Enough Approvals
|
|
243
|
+
### Committing Before Enough Approvals (412)
|
|
292
244
|
|
|
293
|
-
**Symptom**: `POST
|
|
245
|
+
**Symptom**: `POST /iga/change-requests/{id}/commit` returns **412 Precondition Failed**.
|
|
294
246
|
|
|
295
|
-
**Cause**: The
|
|
247
|
+
**Cause**: The CR is under threshold, or a dependency has not committed yet.
|
|
296
248
|
|
|
297
|
-
**Fix**:
|
|
249
|
+
**Fix**: Only commit CRs where `readyToCommit === true`. If still short, collect more approvals; if `blocked`, commit the CR named in `dependsOn` first (see the commit-in-passes loop in `canon/iga-change-requests-api.md`).
|
|
298
250
|
|
|
299
251
|
---
|
|
300
252
|
|
|
301
|
-
###
|
|
253
|
+
### Re-signing the Same CR (409)
|
|
302
254
|
|
|
303
|
-
**Symptom**:
|
|
255
|
+
**Symptom**: `POST /iga/change-requests/{id}/authorize` returns **409 Conflict**.
|
|
304
256
|
|
|
305
|
-
**Cause**:
|
|
257
|
+
**Cause**: Four-eyes enforcement — the same admin cannot sign a CR twice, or the CR is no longer PENDING.
|
|
306
258
|
|
|
307
|
-
**Fix**:
|
|
259
|
+
**Fix**: Have a *different* admin authorize, or refresh the list; do not retry the same admin's signature.
|
|
308
260
|
|
|
309
261
|
---
|
|
310
262
|
|
|
@@ -314,31 +266,21 @@ A working IGA admin panel must include:
|
|
|
314
266
|
|
|
315
267
|
**Cause**: Admin token not included or expired.
|
|
316
268
|
|
|
317
|
-
**Fix**: Obtain a fresh admin token before each batch of calls
|
|
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.
|
|
269
|
+
**Fix**: Obtain a fresh admin token before each batch of calls and handle token refresh.
|
|
328
270
|
|
|
329
271
|
---
|
|
330
272
|
|
|
331
|
-
###
|
|
273
|
+
### Calling a Deprecated add-review / add-rejection Endpoint
|
|
332
274
|
|
|
333
|
-
**Symptom**:
|
|
275
|
+
**Symptom**: `POST /tideAdminResources/add-review` or `/add-rejection` returns `410 Gone`.
|
|
334
276
|
|
|
335
|
-
**Cause**:
|
|
277
|
+
**Cause**: Both endpoints were removed in the IGA decoupling. They are no longer functional.
|
|
336
278
|
|
|
337
|
-
**Fix**:
|
|
279
|
+
**Fix**: Approve via `POST /iga/change-requests/{id}/authorize` (passing the enclave-signed approval in the body) and reject via `POST /iga/change-requests/{id}/deny`.
|
|
338
280
|
|
|
339
281
|
---
|
|
340
282
|
|
|
341
283
|
## References
|
|
342
284
|
|
|
343
|
-
-
|
|
344
|
-
-
|
|
285
|
+
- Current surface: `IgaAdminResource` (`tidecloak-iga-extensions` iga-core) — `/iga/change-requests/*`, `/iga/forseti-contracts`, `/iga/role-policies`.
|
|
286
|
+
- Legacy bootstrap-compat surface: `/tide-admin/change-set/*`, still used by `localtest/init/init_tidecloak.sh` and the `tidecloak-js` `AdminAPI` client.
|
|
@@ -21,7 +21,26 @@ Complete guide to implementing server-side JWT verification for Tide.
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Recommended First: the Shipped `verifyTideCloakToken` Helper
|
|
25
|
+
|
|
26
|
+
Before hand-rolling anything, know that Tide ships a one-call verifier. `@tidecloak/verify` exports `verifyTideCloakToken`, re-exported via `@tidecloak/nextjs/server`:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { verifyTideCloakToken } from '@tidecloak/nextjs/server'; // or '@tidecloak/verify'
|
|
30
|
+
import tcConfig from '../data/tidecloak.json';
|
|
31
|
+
|
|
32
|
+
// Arg order: (config, token, allowedRoles=[]) — config FIRST.
|
|
33
|
+
const payload = await verifyTideCloakToken(tcConfig, token, ['admin']);
|
|
34
|
+
// payload = decoded claims on success, `false` on any failure.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
It does local-JWKS signature verification (embedded `jwk`), issuer + `azp` validation, time-claim checks with `clockTolerance`, and optional role enforcement across realm and client roles. For the majority of API-protection needs this is all you require — see [protect-api-nextjs.md](protect-api-nextjs.md).
|
|
38
|
+
|
|
39
|
+
The complete manual implementation below is the **"under the hood" alternative**. Reach for it when you need something `verifyTideCloakToken` does not do: app-side DPoP proof re-verification, distinct 401 vs 403 responses, `iat` future-dating rejection, or an Express integration.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Complete Implementation (Manual)
|
|
25
44
|
|
|
26
45
|
If you already created `lib/auth/tidecloakConfig.ts`, `lib/auth/tideJWT.ts`, and `lib/auth/protect.ts` from [protect-api-nextjs.md](protect-api-nextjs.md), the versions below **replace** them. This playbook adds DPoP verification, client-role checking in `hasRole`, `extractToken` (supports both Bearer and DPoP schemes), and `iat` future-validation.
|
|
27
46
|
|
|
@@ -48,7 +48,7 @@ Copy-paste this prompt to an AI coding agent to scaffold a customer portal with
|
|
|
48
48
|
> **If TideCloak is running but checks 2-4 fail**, follow the `tide-setup` skill to complete setup. Use playbook `add-auth-nextjs-fresh`.
|
|
49
49
|
>
|
|
50
50
|
> **Package versions** (before writing package.json):
|
|
51
|
-
> 1. Run `npm view @tidecloak/nextjs version` to get the current stable version. Pin it exactly (e.g., `"0.13.
|
|
51
|
+
> 1. Run `npm view @tidecloak/nextjs version` to get the current stable version. Pin it exactly (e.g., `"0.13.33"`). Do not use `"latest"` or `"^"` for `@tidecloak/*` packages.
|
|
52
52
|
> 2. If the resolved version is 0.99.x, skip it. Use the highest non-0.99.x version instead. If unsure, fall back to the versions in `canon/version-policy.md`.
|
|
53
53
|
> 3. Run `npm view next version` and `npm view react version` to get current stable framework versions. Use caret ranges (e.g., `"^15.0.0"`). Align React and React DOM to the same major.
|
|
54
54
|
> 4. Do not use `--force` or `--legacy-peer-deps` as the default install strategy. If peer dependency conflicts arise, resolve them by aligning versions.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Prompt: Security Gap Analysis of an Existing System
|
|
2
|
+
|
|
3
|
+
Copy-paste this to an AI coding agent to audit an existing application for identity, key-management, authorization, and data-exposure gaps — and map each gap to the Tide capability that removes it. Works on systems with no Tide involvement at all.
|
|
4
|
+
|
|
5
|
+
This is an **advisory, read-only** analysis. It does not modify the target.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Prompt
|
|
10
|
+
|
|
11
|
+
> Perform a security gap analysis of this system. You are acting as the **Security Analyst** role from the Tide agent pack. Do not modify any code. Produce a report; do not begin remediation until I approve it.
|
|
12
|
+
>
|
|
13
|
+
> **First, confirm scope**: state exactly what you will inspect (repo, running instance, config) and that this is my own system or an authorized engagement. Read-only.
|
|
14
|
+
>
|
|
15
|
+
> **Load the doctrine**: read `canon/security-gap-mapping.md` (the SG-01 … SG-18 gap table) and `skills/tide-security-analyst/SKILL.md`. If you have the Tide MCP server, call `tide_security_analysis` to load them at once.
|
|
16
|
+
>
|
|
17
|
+
> **Two tiers**: run the **static** sweep (code/config/schema) always. Only run the **runtime confirmation** tier — sending live probes to confirm findings — if I confirm I own or am authorized to test the target, and then strictly per `canon/security-runtime-probes.md` (non-destructive, no exploitation, my own test account only). If I have not authorized live testing, stay static-only and keep findings tagged INFERRED.
|
|
18
|
+
>
|
|
19
|
+
> **Inventory the trust architecture first** (before pattern-matching), answering from the code/config:
|
|
20
|
+
> 1. Identity — how are users authenticated, where do credentials live?
|
|
21
|
+
> 2. Authority — how are tokens/sessions signed, who holds the signing key?
|
|
22
|
+
> 3. Authorization — where is the access decision made: client, server, or both?
|
|
23
|
+
> 4. Data — what sensitive data exists, and who can read it in the clear?
|
|
24
|
+
>
|
|
25
|
+
> **Run the full gap sweep**: work through SG-01 … SG-12 exhaustively. For each, run the detection commands from `canon/security-gap-mapping.md` against the target and record hits. Do NOT sample APIs (SG-05) — enumerate every route and diff against the routes that actually verify. Also collect anything matching the "Out of scope" table.
|
|
26
|
+
>
|
|
27
|
+
> **Every finding must have**:
|
|
28
|
+
> - A named **trust concentration** — the single party or artifact that, once compromised, defeats the control. "Uses passwords" is not a finding; "password verification collapses to one database + one server process" is.
|
|
29
|
+
> - **Evidence** — a file:line, config value, schema column, or HTTP observation — tagged VERIFIED (observed), INFERRED (implied by code), or ASSUMED (my statement, unconfirmed).
|
|
30
|
+
> - A **severity** (CRITICAL / HIGH / MEDIUM / INFO) based on blast radius under a single compromise.
|
|
31
|
+
> - The **Tide replacement**, cited from `canon/feature-mapping.md` or `canon/invariants.md`, with its sourcing tag.
|
|
32
|
+
> - The **remediation playbook sequence** (verify each playbook exists via `tide_list playbooks`).
|
|
33
|
+
> - The **limits** — the honesty note: what the Tide replacement does NOT cover.
|
|
34
|
+
>
|
|
35
|
+
> **Be honest**: include a non-empty "Not Addressed by Tide" section for anything you find that Tide doesn't fix (injection, XSS, CSRF, vulnerable dependencies, rate limiting, infra hardening). Never present Tide as fixing these. An honest analysis is more credible than an inflated one.
|
|
36
|
+
>
|
|
37
|
+
> **Report in the format** defined in `skills/tide-security-analyst/SKILL.md` (Report Format section): trust-architecture summary, findings most-severe-first, gap interactions, out-of-scope section, then a recommended remediation sequence with the execution role to engage first (per I-18).
|
|
38
|
+
>
|
|
39
|
+
> **Do not**: modify code, run TideCloak bootstrap, install the SDK, or start fixing anything. Report only. When I approve findings, hand off to the execution roles (Setup → Application → Security → IAM/Policy → Reviewer).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Acceptance Criteria
|
|
44
|
+
|
|
45
|
+
- [ ] Scope and authorization confirmed before inspection
|
|
46
|
+
- [ ] Trust architecture (identity / authority / authorization / data) documented
|
|
47
|
+
- [ ] All of SG-01 … SG-18 checked, not sampled (or non-applicability stated per SG)
|
|
48
|
+
- [ ] Every finding names a trust concentration and carries evidence with a confidence tag
|
|
49
|
+
- [ ] Runtime tier run only if authorized; probed findings carry a Runtime confirmation line; unprobed findings stay INFERRED
|
|
50
|
+
- [ ] Every Tide-replacement claim cites `canon/feature-mapping.md` or `canon/invariants.md`
|
|
51
|
+
- [ ] Every remediation names a playbook that exists in `playbooks/`
|
|
52
|
+
- [ ] Non-empty "Not Addressed by Tide" section (or explicit "none found in scope")
|
|
53
|
+
- [ ] No finding claims Tide fixes an out-of-scope gap class
|
|
54
|
+
- [ ] Remediation sequence provided with first execution role named
|
|
55
|
+
- [ ] No code was modified; the analysis is read-only
|
|
@@ -10,7 +10,7 @@ Scenario-specific mistakes for encrypted communication apps using the hybrid Tid
|
|
|
10
10
|
|
|
11
11
|
**Why it fails**: The private key is the root of trust for all E2E operations. If stored in plaintext, a database breach or XSS attack exposes all encrypted communications.
|
|
12
12
|
|
|
13
|
-
**Fix**: Always encrypt the private key with `doEncrypt(privateKeyBytes, tag)` before storing. Always decrypt with `doDecrypt()` on login. Never persist the decrypted private key outside of in-memory variables.
|
|
13
|
+
**Fix**: Always encrypt the private key with `doEncrypt([{ data: privateKeyBytes, tags: [tag] }])` before storing. Always decrypt with `doDecrypt()` on login. Never persist the decrypted private key outside of in-memory variables.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -118,6 +118,6 @@ Scenario-specific mistakes for encrypted communication apps using the hybrid Tid
|
|
|
118
118
|
|
|
119
119
|
**Mistake**: Installing a 0.99.x pre-release version of the Tide SDK.
|
|
120
120
|
|
|
121
|
-
**Why it fails**: 0.99.x versions are unstable pre-releases. The stable version is 0.13.
|
|
121
|
+
**Why it fails**: 0.99.x versions are unstable pre-releases. The stable version is 0.13.33 (per pack version policy).
|
|
122
122
|
|
|
123
|
-
**Fix**: Pin `@tidecloak/react` to `0.13.
|
|
123
|
+
**Fix**: Pin `@tidecloak/react` to `0.13.33`. See `canon/version-policy.md`.
|
|
@@ -117,12 +117,12 @@
|
|
|
117
117
|
|
|
118
118
|
23. Implement first-login flow:
|
|
119
119
|
- Generate keypair client-side
|
|
120
|
-
- Encrypt private key with `doEncrypt(privateKeyBytes, tag)`
|
|
120
|
+
- Encrypt private key with `doEncrypt([{ data: privateKeyBytes, tags: [tag] }])`
|
|
121
121
|
- Store encrypted private key + public key via protected API
|
|
122
122
|
|
|
123
123
|
24. Implement returning-login flow:
|
|
124
124
|
- Fetch encrypted key material via protected API
|
|
125
|
-
- Decrypt private key with `doDecrypt(encryptedPrivateKey, tag)`
|
|
125
|
+
- Decrypt private key with `doDecrypt([{ encrypted: encryptedPrivateKey, tags: [tag] }])`
|
|
126
126
|
- Private key available in memory for runtime E2E operations
|
|
127
127
|
|
|
128
128
|
25. Implement runtime E2E operations using the external crypto library (not Tide).
|
|
@@ -77,14 +77,14 @@ On **first login** for a new user:
|
|
|
77
77
|
1. User authenticates via TideCloak SSO
|
|
78
78
|
2. App checks database for existing encrypted key material
|
|
79
79
|
3. No keys found → app generates a new asymmetric keypair client-side (e.g., `crypto_box_keypair()`)
|
|
80
|
-
4. App encrypts the private key using `doEncrypt(privateKeyBytes, tag)` (Tide self-encryption)
|
|
80
|
+
4. App encrypts the private key using `doEncrypt([{ data: privateKeyBytes, tags: [tag] }])` (Tide self-encryption)
|
|
81
81
|
5. App stores the encrypted private key and the public key in the database via a protected API endpoint
|
|
82
82
|
6. Private key is now available in memory for runtime E2E operations
|
|
83
83
|
|
|
84
84
|
On **returning login**:
|
|
85
85
|
1. User authenticates via TideCloak SSO
|
|
86
86
|
2. App fetches encrypted key material from database
|
|
87
|
-
3. App decrypts the private key using `doDecrypt(encryptedPrivateKey, tag)` (Tide self-encryption)
|
|
87
|
+
3. App decrypts the private key using `doDecrypt([{ encrypted: encryptedPrivateKey, tags: [tag] }])` (Tide self-encryption)
|
|
88
88
|
4. Private key is now available in memory for runtime E2E operations
|
|
89
89
|
|
|
90
90
|
**Runtime E2E operations** (not Tide — handled by external crypto library):
|
|
@@ -27,11 +27,11 @@ All steps must complete before the service can sign merge commits. Order matters
|
|
|
27
27
|
- `POST /admin/realms/{realm}/tide-admin/toggle-iga` with form-urlencoded `isIGAEnabled=true`.
|
|
28
28
|
- Must happen after licensing.
|
|
29
29
|
|
|
30
|
-
## Phase 3: Approve initial change
|
|
30
|
+
## Phase 3: Approve initial change requests
|
|
31
31
|
|
|
32
|
-
6. **Approve
|
|
33
|
-
- List: `GET /admin/realms/{realm}/
|
|
34
|
-
-
|
|
32
|
+
6. **Approve initial change requests** (current `/iga/change-requests/...` surface — see `canon/iga-change-requests-api.md`)
|
|
33
|
+
- List: `GET /admin/realms/{realm}/iga/change-requests?status=PENDING` (objects keyed by `id`).
|
|
34
|
+
- Approve: `POST .../iga/change-requests/bulk-authorize` (`{"actionTypeIn":["CREATE","DELETE"],"limit":100}`), then `POST .../iga/change-requests/{id}/commit` for each `readyToCommit` CR. FirstAdmin bootstrap signs server-side.
|
|
35
35
|
|
|
36
36
|
## Phase 4: Admin user setup
|
|
37
37
|
|
|
@@ -50,11 +50,11 @@ All steps must complete before the service can sign merge commits. Order matters
|
|
|
50
50
|
10. **Wait for account linking**
|
|
51
51
|
- Poll `GET /admin/realms/{realm}/users/{userId}` until `attributes.tideUserKey` and `attributes.vuid` are present for each admin.
|
|
52
52
|
|
|
53
|
-
## Phase 5: Approve user change
|
|
53
|
+
## Phase 5: Approve user change requests
|
|
54
54
|
|
|
55
|
-
11. **Approve user change
|
|
56
|
-
- List: `GET /admin/realms/{realm}/
|
|
57
|
-
-
|
|
55
|
+
11. **Approve user change requests**
|
|
56
|
+
- List: `GET /admin/realms/{realm}/iga/change-requests?status=PENDING`.
|
|
57
|
+
- Authorize then commit (see Phase 3 / `canon/iga-change-requests-api.md`).
|
|
58
58
|
|
|
59
59
|
## Phase 6: IDP settings and adapter export
|
|
60
60
|
|