@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/GAP_REGISTER.md +18 -8
  3. package/PRIVACY.md +41 -0
  4. package/README.md +204 -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
@@ -147,7 +147,7 @@ For missing Tide adapter (Cause 5):
147
147
  ```bash
148
148
  # Re-export adapter with Tide extensions
149
149
  curl -X GET \
150
- "${TIDECLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_ID}/vendorResources/get-installations-provider?providerId=keycloak-oidc-keycloak-json" \
150
+ "${TIDECLOAK_URL}/admin/realms/${REALM}/vendorResources/get-installations-provider?clientId=${CLIENT_ID}&providerId=keycloak-oidc-keycloak-json" \
151
151
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
152
152
  > data/tidecloak.json
153
153
 
@@ -176,7 +176,7 @@ For missing `jwk` field (Cause 1):
176
176
  ```bash
177
177
  # Re-export adapter with Tide extensions (IGA must be enabled)
178
178
  curl -X GET \
179
- "${TIDECLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_ID}/vendorResources/get-installations-provider?providerId=keycloak-oidc-keycloak-json" \
179
+ "${TIDECLOAK_URL}/admin/realms/${REALM}/vendorResources/get-installations-provider?clientId=${CLIENT_ID}&providerId=keycloak-oidc-keycloak-json" \
180
180
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
181
181
  > data/tidecloak.json
182
182
 
@@ -279,10 +279,10 @@ if (!navigator.onLine) {
279
279
  ```javascript
280
280
  // Test encrypt/decrypt round-trip
281
281
  const plaintext = 'test data';
282
- const ciphertext = await iam.doEncrypt('test', plaintext);
282
+ const [ciphertext] = await iam.doEncrypt([{ data: plaintext, tags: ['test'] }]);
283
283
  console.log('Encrypted:', ciphertext); // Should complete within 2-5 seconds
284
284
 
285
- const decrypted = await iam.doDecrypt('test', ciphertext);
285
+ const [decrypted] = await iam.doDecrypt([{ encrypted: ciphertext, tags: ['test'] }]);
286
286
  console.log('Decrypted:', decrypted); // Should match plaintext
287
287
  assert(decrypted === plaintext);
288
288
  ```
@@ -474,15 +474,10 @@ curl -H "Authorization: DPoP $TOKEN" -H "DPoP: $DPOP" https://app.example.com/ap
474
474
  curl "${TIDECLOAK_URL}/admin/realms/${REALM}" | jq '.igaEnabled // "not enabled"'
475
475
  # Should output: true
476
476
 
477
- # 2. Check pending change-sets
478
- curl "${TIDECLOAK_URL}/admin/realms/${REALM}/tide-admin/change-set/all/requests" \
477
+ # 2. Check pending change requests (current /iga/ surface — see canon/iga-change-requests-api.md)
478
+ curl "${TIDECLOAK_URL}/admin/realms/${REALM}/iga/change-requests?status=PENDING" \
479
479
  -H "Authorization: Bearer ${ADMIN_TOKEN}" | jq '.'
480
- # Look for change-sets in 'PENDING' or 'APPROVED' status
481
-
482
- # 3. Check counts (lightweight)
483
- curl "${TIDECLOAK_URL}/admin/realms/${REALM}/tide-admin/change-set/counts" \
484
- -H "Authorization: Bearer ${ADMIN_TOKEN}"
485
- # Returns: {"users":N,"roles":N,"clients":N,"groups":N,"total":N}
480
+ # Objects keyed by .id, with .status, .readyToCommit
486
481
  ```
487
482
 
488
483
  **Fix**:
@@ -494,25 +489,20 @@ curl -X POST "${TIDECLOAK_URL}/admin/realms/${REALM}/tide-admin/toggle-iga" \
494
489
  -H "Authorization: Bearer ${ADMIN_TOKEN}"
495
490
  ```
496
491
 
497
- For change-set not approved (Cause 2):
492
+ For a change request not approved (Cause 2):
498
493
  ```bash
499
- # Sign (approve) the change-set
500
- curl -X POST "${TIDECLOAK_URL}/admin/realms/${REALM}/tide-admin/change-set/sign/batch" \
501
- -H "Authorization: Bearer ${ADMIN_TOKEN}" \
502
- -H "Content-Type: application/json" \
503
- -d '{"changeSets":[{"changeSetId":"DRAFT_RECORD_ID","changeSetType":"USER_ROLE","actionType":"CREATE"}]}'
504
-
505
- # If MultiAdmin: response has requiresApprovalPopup=true — complete enclave signing
506
- # Repeat with different admin tokens until quorum reached
494
+ # Authorize (approve) — CR_ID is the .id from the list above; body {} optional
495
+ curl -X POST "${TIDECLOAK_URL}/admin/realms/${REALM}/iga/change-requests/${CR_ID}/authorize" \
496
+ -H "Authorization: Bearer ${ADMIN_TOKEN}" -H "Content-Type: application/json" -d '{}'
497
+ # 409 = you already signed (four-eyes). Tide MultiAdmin: use the {id}/approval-model enclave exchange.
498
+ # Repeat with different admin tokens until threshold reached.
507
499
  ```
508
500
 
509
- For change-set not committed (Cause 3):
501
+ For a change request not committed (Cause 3):
510
502
  ```bash
511
- # Commit after quorum approval
512
- curl -X POST "${TIDECLOAK_URL}/admin/realms/${REALM}/tide-admin/change-set/commit/batch" \
513
- -H "Authorization: Bearer ${ADMIN_TOKEN}" \
514
- -H "Content-Type: application/json" \
515
- -d '{"changeSets":[{"changeSetId":"DRAFT_RECORD_ID","changeSetType":"USER_ROLE","actionType":"CREATE"}]}'
503
+ # Commit after threshold reached (readyToCommit==true); 412 if still short
504
+ curl -X POST "${TIDECLOAK_URL}/admin/realms/${REALM}/iga/change-requests/${CR_ID}/commit" \
505
+ -H "Authorization: Bearer ${ADMIN_TOKEN}"
516
506
  ```
517
507
 
518
508
  For token not refreshed (Cause 5):
@@ -873,10 +863,10 @@ curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
873
863
  -H "Authorization: Bearer $TOKEN" | jq '.[0].id'
874
864
  # If null: user change request was not approved
875
865
 
876
- # Check pending user change requests
877
- curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/users/requests" \
866
+ # Check pending change requests (current /iga/ surface)
867
+ curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/iga/change-requests?status=PENDING" \
878
868
  -H "Authorization: Bearer $TOKEN" | jq length
879
- # If > 0: approve them first
869
+ # If > 0: approve them first (authorize + commit — see canon/iga-change-requests-api.md)
880
870
  ```
881
871
 
882
872
  **Fix**: Approve user creation change request between user creation and role assignment:
@@ -925,7 +915,7 @@ jq 'has("jwk") and has("vendorId") and has("homeOrkUrl")' data/tidecloak.json
925
915
 
926
916
  # 2. Check export endpoint used
927
917
  # Correct endpoint:
928
- # GET /admin/realms/{realm}/clients/{client-id}/vendorResources/get-installations-provider?providerId=keycloak-oidc-keycloak-json
918
+ # GET /admin/realms/{realm}/vendorResources/get-installations-provider?clientId={clientId}&providerId=keycloak-oidc-keycloak-json (realm-level; client is a query param)
929
919
  ```
930
920
 
931
921
  **Fix**:
@@ -933,7 +923,7 @@ jq 'has("jwk") and has("vendorId") and has("homeOrkUrl")' data/tidecloak.json
933
923
  ```bash
934
924
  # Use correct Tide adapter export endpoint
935
925
  curl -X GET \
936
- "${TIDECLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_ID}/vendorResources/get-installations-provider?providerId=keycloak-oidc-keycloak-json" \
926
+ "${TIDECLOAK_URL}/admin/realms/${REALM}/vendorResources/get-installations-provider?clientId=${CLIENT_ID}&providerId=keycloak-oidc-keycloak-json" \
937
927
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
938
928
  -o data/tidecloak.json
939
929
 
@@ -1217,8 +1207,8 @@ await tc.executeSignRequest(request, true);
1217
1207
  | `requiresApprovalPopup: true` | Backend cannot auto-approve, must open approval enclave in admin's browser |
1218
1208
  | `Cannot destructure property 'Policy' of 'Models' as it is undefined` | `Models` imported from `@tidecloak/nextjs` (wrong package). Import from `@tideorg/js` instead. See T-15. |
1219
1209
  | `IAMService.createTideRequest is not a function` | `createTideRequest` (and `requestTideOperatorApproval`, `executeSignRequest`) live on `IAMService._tc` (TideCloak), not `IAMService`. See T-16. |
1220
- | `Cannot destructure 'verifyTideCloakToken'` | `@tidecloak/verify` is CJS, use defensive interop pattern |
1221
- | "Could not create tide realm" (email is null) | `setUpTideRealm` expects `Content-Type: application/x-www-form-urlencoded` with `email`, not JSON |
1210
+ | `Cannot destructure 'verifyTideCloakToken'` | Very old `@tidecloak/verify` (single-export build); upgrade — current versions ship dual CJS/ESM exports and a plain named import works. Note arg order is `(config, token, allowedRoles?)`. |
1211
+ | "Tide realm setup failed" / "Could not set up the Tide realm" | `setUpTideRealm` expects `Content-Type: application/x-www-form-urlencoded` with `email` (`@FormParam`), not JSON. (There is no explicit email-null guard; a missing/blank email surfaces as a downstream setup failure.) |
1222
1212
  | "Could not find configuration for Required Action" | Missing Tide env vars (`SYSTEM_HOME_ORK`, `USER_HOME_ORK`, `THRESHOLD_T`, `THRESHOLD_N`, `PAYER_PUBLIC`) from Docker |
1223
1213
  | AccessDeniedException on H2 | Data directory mounted as project root or wrong permissions; mount `./data` subdirectory and `chown 1000:1000` |
1224
1214
  | `"User has not been given any access to '${tag}'"` | User lacks `_tide_{tag}.selfencrypt` or `.selfdecrypt` role for self-encryption. Fatal — assign role, then re-login. |
@@ -1250,9 +1240,9 @@ curl -s "$TIDECLOAK_URL/admin/realms/$REALM/roles/default-roles-$REALM" \
1250
1240
  -H "Authorization: Bearer $TOKEN" | jq '.composites.realm'
1251
1241
 
1252
1242
  # 3. Check IGA change requests are approved
1253
- curl -s "$TIDECLOAK_URL/admin/realms/$REALM/tide-admin/change-set/roles/requests" \
1243
+ curl -s "$TIDECLOAK_URL/admin/realms/$REALM/iga/change-requests?status=PENDING" \
1254
1244
  -H "Authorization: Bearer $TOKEN" | jq length
1255
- # Should be 0. If > 0, approve and commit.
1245
+ # Should be 0. If > 0, authorize and commit (canon/iga-change-requests-api.md).
1256
1246
 
1257
1247
  # 4. Check user's JWT has the roles
1258
1248
  # Decode token and check realm_access.roles
@@ -1279,12 +1269,15 @@ curl -s "$TIDECLOAK_URL/admin/realms/$REALM/tide-admin/change-set/roles/requests
1279
1269
 
1280
1270
  **Diagnostics**:
1281
1271
  ```bash
1282
- # 1. Check admin-policy endpoint is reachable
1283
- curl -s "${TIDECLOAK_URL}/realms/${REALM}/tide-policy-resources/admin-policy"
1284
- # Should return base64-encoded bytes, not HTML or error JSON
1272
+ # 1. Check the role-policies endpoint is reachable (admin bearer required)
1273
+ curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
1274
+ "${TIDECLOAK_URL}/admin/realms/${REALM}/iga/role-policies"
1275
+ # Should return a JSON array of records; each carries the signed bytes in its `policy` field.
1276
+ # NOTE: the old public GET /realms/{realm}/tide-policy-resources/admin-policy endpoint
1277
+ # is NOT present on current main; do not rely on it.
1285
1278
 
1286
1279
  # 2. Check URL construction (common: double slash from trailing slash in auth-server-url)
1287
- echo "${TIDECLOAK_URL}/realms/${REALM}/tide-policy-resources/admin-policy"
1280
+ echo "${TIDECLOAK_URL}/admin/realms/${REALM}/iga/role-policies"
1288
1281
  # Must not contain // between host and path (except after protocol)
1289
1282
 
1290
1283
  # 3. Check if addPolicy was called AFTER approval, not before
@@ -1294,15 +1287,18 @@ grep -B 5 "addPolicy" src/
1294
1287
 
1295
1288
  **Fix**:
1296
1289
 
1297
- 1. **Fetch admin policy from the correct endpoint** (no auth required):
1290
+ 1. **Fetch the signed admin policy from the correct endpoint** (server-side, admin bearer token):
1298
1291
  ```
1299
- GET /realms/{realm}/tide-policy-resources/admin-policy
1292
+ GET /admin/realms/{realm}/iga/role-policies
1300
1293
  ```
1301
- Not: `GET /admin/realms/{realm}/tide-admin/realm-policy` (returns `{ status: "none" }`).
1294
+ The signed policy bytes live in each record's `policy` field. Not: the public
1295
+ `GET /realms/{realm}/tide-policy-resources/admin-policy` endpoint (not on current main),
1296
+ nor `GET /admin/realms/{realm}/tide-admin/realm-policy` (returns `{ status: "none" }`).
1302
1297
 
1303
- 2. **Decode base64 response**:
1298
+ 2. **Decode the base64 `policy` field**:
1304
1299
  ```js
1305
- const b64 = await res.text();
1300
+ const records = await res.json();
1301
+ const b64 = records[0].policy; // signed bytes, base64-encoded
1306
1302
  const raw = Buffer.from(b64, "base64");
1307
1303
  const bytes = new Uint8Array(raw);
1308
1304
  ```
@@ -1318,7 +1314,7 @@ grep -B 5 "addPolicy" src/
1318
1314
  4. **URL normalization**: Strip trailing slashes from `auth-server-url` before constructing endpoint URLs:
1319
1315
  ```js
1320
1316
  const baseUrl = config["auth-server-url"].replace(/\/+$/, "");
1321
- const adminPolicyUrl = `${baseUrl}/realms/${config.realm}/tide-policy-resources/admin-policy`;
1317
+ const rolePoliciesUrl = `${baseUrl}/admin/realms/${config.realm}/iga/role-policies`;
1322
1318
  ```
1323
1319
 
1324
1320
  **This is a bootstrap/policy-flow failure, not a role issue.** Do not attempt to fix by renaming roles or switching encryption models.
@@ -1453,17 +1449,17 @@ VERIFIED (TIDE_LEARNINGS-001 L-05).
1453
1449
 
1454
1450
  ---
1455
1451
 
1456
- ## T-18: IGA Change-Set Approval May Require Admin Console UI
1452
+ ## T-18: IGA Change-Request Approval May Require Admin Console UI (Tide MultiAdmin)
1457
1453
 
1458
- **Symptom**: REST-based `POST /tide-admin/change-set/sign` succeeds but `POST /tide-admin/change-set/commit` fails with "Could not find authorization signature."
1454
+ **Symptom**: REST-based `POST /iga/change-requests/{id}/authorize` succeeds but `commit` fails (or the CR never reaches `readyToCommit`) with "Could not find authorization signature."
1459
1455
 
1460
- **Root cause**: IGA approval is a cryptographic operation requiring the admin's Tide doken and ORK network interaction. In some configurations, the REST-based sign endpoint auto-signs but the commit requires the browser-based SDK (`approveTideRequests()` via the admin console UI).
1456
+ **Root cause**: In Tide MultiAdmin mode, approval is a cryptographic operation requiring the admin's Tide doken and ORK interaction a bare `authorize` is not enough; the admin must complete the two-phase `{id}/approval-model` enclave exchange. FirstAdmin/Tideless mode signs server-side on `authorize`, so bootstrap works headless.
1461
1457
 
1462
- **When this happens**: Typically when the admin has a linked Tide identity and IGA requires the cryptographic round-trip. The bootstrap script's REST-based `approve_and_commit` may work for the initial bootstrap (master admin token) but fail for subsequent operations.
1458
+ **When this happens**: Typically once the realm is in Tide MultiAdmin mode (a `tide-realm-admin` exists and IGA requires the cryptographic round-trip). The bootstrap script's `approve_all_pending` works during initial FirstAdmin bootstrap but a headless `authorize` fails for subsequent MultiAdmin operations.
1463
1459
 
1464
- **Fix**: If REST commit fails, approve and commit change requests from the TideCloak admin console UI: `http://localhost:8080/admin/master/console/#/{realm}`. Navigate to the change request section and approve from the UI.
1460
+ **Fix**: Complete the enclave step `GET`/`POST /iga/change-requests/{id}/approval-model` with the SDK/enclave, or approve from the TideCloak admin console UI (`http://localhost:8080/admin/master/console/#/{realm}` Change Requests).
1465
1461
 
1466
- **Verification**: After approval, `GET /tide-admin/change-set/counts` returns `total: 0`.
1462
+ **Verification**: After approval, `GET /iga/change-requests?status=PENDING` returns `[]`.
1467
1463
 
1468
1464
  VERIFIED (TIDE_LEARNINGS-001 L-11).
1469
1465
 
@@ -1483,7 +1479,7 @@ VERIFIED (TIDE_LEARNINGS-001 L-11).
1483
1479
  5. Check TideCloak container logs: `docker logs <container>` for the real error
1484
1480
 
1485
1481
  **Common 500 causes**:
1486
- - ORK PreSign failure (`Provided TideAuthData OR PreviousTokenAuthorization`) stale realm, recreate it
1482
+ - ORK PreSign failure — on a stale realm, recreate it. Actual PreSign error strings on current ORK include: `"Tide user authentication data provided but no valid authentication method found within"` and `"DPoP thumbprint in requested token does not match with what the tide user approved"` (`ork: TidecloakSessionStartTokenSignRequest.cs`). (The older `Provided TideAuthData OR PreviousTokenAuthorization` string is no longer emitted.)
1487
1483
  - Database connection failure
1488
1484
  - Realm misconfiguration after incomplete bootstrap
1489
1485
 
@@ -1578,3 +1574,69 @@ VERIFIED (Ratidefy SaaS — IDP settings unsigned, client-origin-auth key had si
1578
1574
  **Verification**: Role shows "ACTIVE" status in TideCloak admin after sign+commit.
1579
1575
 
1580
1576
  VERIFIED (Ratidefy SaaS — provisioner assigned tide-realm-admin before account linking, creating permanent DRAFT).
1577
+
1578
+ ---
1579
+
1580
+ ## T-23: ASP.NET Core JWT Validation Fails on Tidecloak EdDSA Tokens
1581
+
1582
+ **Symptoms:**
1583
+ - `IDX10503: Signature validation failed`
1584
+ - `IDX10500: Signature validation failed. No security keys were provided`
1585
+ - 401 on every request despite a valid `Authorization: Bearer <token>`
1586
+
1587
+ **Cause:** `Microsoft.IdentityModel.Tokens` does not ship EdDSA. The `Tide.Asgard.AspNetCore.Authentication` SDK ships an EdDSA `SignatureProvider` in `Tide.Asgard.Core`, but you have to wire it in by setting `IssuerSigningKey = Utils.GetEd25519IssuerKey(builder.Configuration)`.
1588
+
1589
+ The asgard `Example/Program.cs` has that exact line **commented out** at line 17. Copying the Example verbatim leaves you with no EdDSA support; the README form at lines 117-139 is the correct wiring.
1590
+
1591
+ **Fix:**
1592
+ ```csharp
1593
+ builder.Services.AddKeycloakWebApiAuthentication(builder.Configuration, options =>
1594
+ {
1595
+ options.RequireHttpsMetadata = false;
1596
+ options.TokenValidationParameters.IssuerSigningKey =
1597
+ Utils.GetEd25519IssuerKey(builder.Configuration);
1598
+ });
1599
+ ```
1600
+
1601
+ **Verification:** Tokens with `alg=EdDSA` validate. Logs at Debug for `Keycloak.AuthServices` show the validation chain completing.
1602
+
1603
+ VERIFIED (asgard `README.md:117-139` vs `Example/Program.cs:17`).
1604
+
1605
+ See [playbooks/protect-aspnet-core-asgard.md](../playbooks/protect-aspnet-core-asgard.md).
1606
+
1607
+ ---
1608
+
1609
+ ## T-24: ASP.NET Core Token Exchange Throws NotImplementedException
1610
+
1611
+ **Symptoms:**
1612
+ - `ITokenExchangeService.ExchangeToken(...)` throws `NotImplementedException` from the asgard SDK.
1613
+ - Worked before; broke after enabling DPoP on the browser SPA.
1614
+
1615
+ **Cause:** `TokenExchangeService.ExchangeToken` inspects the inbound `Authorization` header. Bearer tokens are routed to a fully-implemented path. DPoP-tagged requests are routed to `ExchangeDPoPToken`, which is a stub (`throw new NotImplementedException()` at the top of the method). The same applies to the "Doken" auth scheme (`ExchangeTideDoken` stub). The internal `ValidateExchangeProof` helper is also a stub.
1616
+
1617
+ The most common trigger is uncommenting `useDPoP: { mode: "strict", alg: "EdDSA" }` in the SPA's `kc.init({...})` call. The moment the SPA starts sending `Authorization: DPoP <token>` + `DPoP: <proof>` headers, every controller that calls `ExchangeToken` 500s.
1618
+
1619
+ **Fix:** Disable browser DPoP until the SDK ships a DPoP-aware exchange path. The DPoP proof-validation service (`DPoPProofValidationService`) is fully implemented for **inbound** DPoP, but its registration helper `.WithDPoP(...)` is commented out — so DPoP today requires manual service wiring on inbound requests AND no DPoP on token exchange.
1620
+
1621
+ **Verification:** With `useDPoP` disabled, exchange succeeds. With DPoP on, the 500 is reproducible.
1622
+
1623
+ VERIFIED (asgard `TokenExchangeService.cs:28-35` routes to stub at line 93).
1624
+
1625
+ ---
1626
+
1627
+ ## T-25: ASP.NET Core 401 with Audience Mismatch
1628
+
1629
+ **Symptoms:**
1630
+ - 401 on every browser-issued request despite a valid token.
1631
+ - Token decodes correctly but `aud` claim does not include the backend client ID.
1632
+
1633
+ **Cause:** The browser's public client (`browser-login-page`) issues tokens with itself as the audience. The .NET backend's confidential client (`backend`) rejects tokens that do not list `backend` in `aud`.
1634
+
1635
+ **Fix:** Add an audience mapper to the browser client's dedicated client scope.
1636
+
1637
+ - Realm -> Clients -> `browser-login-page` -> Client scopes -> `browser-login-page-dedicated` -> Add mapper -> By configuration -> Audience.
1638
+ - Name: `backend-mapper`. Included Client Audience: `backend`. Save.
1639
+
1640
+ **Verification:** Decode an SPA-issued token (e.g. at jwt.io); confirm `aud` array contains `backend`.
1641
+
1642
+ VERIFIED (asgard `README.md:55-62`).
@@ -8,22 +8,20 @@ All other pack files point here. Do not duplicate version numbers in playbooks o
8
8
 
9
9
  ## Tide SDK Packages
10
10
 
11
- Resolved from npm registry on 2026-03-30. No 0.99.x versions are used.
11
+ Resolved from npm registry on 2026-07-07. npm is the release source of truth. Repo `package.json` version fields lag npm (e.g. `tidecloak-js` source on `main` reads 0.12.15); always confirm against npm, not the checked-out source tree.
12
12
 
13
13
  | Package | Stable version | Range for templates | Purpose |
14
14
  |---------|---------------|--------------------| --------|
15
- | `@tidecloak/js` | 0.13.26 | `0.13.26` | Core SDK (vanilla JS, IAMService) |
16
- | `@tidecloak/react` | 0.13.26 | `0.13.26` | React hooks and guard components |
17
- | `@tidecloak/nextjs` | 0.13.26 | `0.13.26` | Next.js provider and hooks |
18
- | `@tidecloak/verify` | 0.13.26 | `0.13.26` | Server-side JWT verification (CJS) |
19
- | `heimdall-tide` | 0.13.26 | `0.13.26` | Policy signing, BasicCustomRequest |
20
- | `@tideorg/js` | 0.13.26 | `0.13.26` | Models, Contracts (Forseti) |
21
- | `asgard-tide` | 0.13.26 | `0.13.26` | Signing request builders (`BasicCustomRequest`, `DynamicPayloadCustomRequest`) |
15
+ | `@tidecloak/js` | 0.13.33 | `0.13.33` | Core SDK (vanilla JS, IAMService) |
16
+ | `@tidecloak/react` | 0.13.33 | `0.13.33` | React hooks and guard components |
17
+ | `@tidecloak/nextjs` | 0.13.33 | `0.13.33` | Next.js provider and hooks |
18
+ | `@tidecloak/verify` | 0.13.33 | `0.13.33` | Server-side JWT verification (CJS) |
19
+ | `heimdall-tide` | 0.13.33 | `0.13.33` | Policy signing, BasicCustomRequest |
20
+ | `@tideorg/js` | 0.13.33 | `0.13.33` | Models, Contracts (Forseti). `tide-js` `main` package.json reads 0.13.32; npm latest is 0.13.33. |
21
+ | `asgard-tide` | 0.13.33 | `0.13.33` | Signing request builders (`BasicCustomRequest`, `DynamicPayloadCustomRequest`) |
22
22
 
23
23
  **Pin to exact version in templates.** Tide packages are pre-1.0. Minor bumps can break. Run `npm view <package> version` before installing to confirm the latest stable.
24
24
 
25
- **Exclude all 0.99.x versions.** If any `@tidecloak/*` package publishes a 0.99.x release, do not adopt it in pack guidance until it is confirmed stable.
26
-
27
25
  ---
28
26
 
29
27
  ## Framework Packages
@@ -75,7 +73,6 @@ npm view jose version
75
73
 
76
74
  - **Do not use `latest` tag in package.json.** Pin Tide packages to exact versions. Use caret ranges only for framework packages. This applies to all Tide packages: `@tidecloak/*`, `@tideorg/js`, `heimdall-tide`, `asgard-tide`.
77
75
  - **Do not use `-staging` npm tags.** Tags like `0.13.24-staging` are pre-release builds. Always use the stable version listed above. "Use staging" refers to the Docker image (`tideorg/tidecloak-stg-dev`), never to npm package tags. VERIFIED (LEARNINGS-batch-005 L-01, L-02).
78
- - **Do not use 0.99.x versions** of any `@tidecloak/*` package in templates or guidance.
79
76
  - **Do not assume 1.0.0.** All Tide packages are pre-1.0. Do not write `"@tidecloak/nextjs": "^1.0.0"`.
80
77
  - **Do not hardcode versions in playbook install snippets.** Playbooks should say `npm install @tidecloak/nextjs` (installs latest) and then instruct the builder to verify with `npm view`. Templates pin the version explicitly.
81
78
  - **Docker image and npm versions are independent.** Switching to the staging Docker image (`tidecloak-stg-dev`) does NOT mean switching to staging npm packages. npm packages stay at stable versions.
@@ -86,4 +83,3 @@ npm view jose version
86
83
 
87
84
  - When a new stable Tide SDK release is published.
88
85
  - When a framework major version ships that affects template compatibility.
89
- - When a 0.99.x Tide package appears and a decision is needed.