@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.
Files changed (76) hide show
  1. package/GAP_REGISTER.md +117 -0
  2. package/README.md +38 -0
  3. package/adapters/AGENTS.md +241 -0
  4. package/adapters/CLAUDE.md +146 -0
  5. package/adapters/replit.md +224 -0
  6. package/canon/anti-patterns.md +2133 -0
  7. package/canon/concepts.md +816 -0
  8. package/canon/custom-contracts.md +533 -0
  9. package/canon/delegation.md +195 -0
  10. package/canon/feature-mapping.md +637 -0
  11. package/canon/framework-matrix.md +1125 -0
  12. package/canon/invariants.md +851 -0
  13. package/canon/redirect-handler.md +254 -0
  14. package/canon/tidecloak-bootstrap.md +287 -0
  15. package/canon/tidecloak-endpoints.md +294 -0
  16. package/canon/troubleshooting.md +1494 -0
  17. package/canon/version-policy.md +89 -0
  18. package/mcp-server/dist/index.d.ts +2 -0
  19. package/mcp-server/dist/index.js +605 -0
  20. package/package.json +45 -0
  21. package/playbooks/add-auth-nextjs-existing.md +799 -0
  22. package/playbooks/add-auth-nextjs-fresh.md +654 -0
  23. package/playbooks/add-rbac-nextjs.md +190 -0
  24. package/playbooks/bootstrap-realm-from-template.md +170 -0
  25. package/playbooks/configure-e2ee-roles-and-policies.md +196 -0
  26. package/playbooks/deploy-tidecloak-docker.md +792 -0
  27. package/playbooks/diagnose-broken-login.md +234 -0
  28. package/playbooks/diagnose-missing-roles-or-claims.md +253 -0
  29. package/playbooks/initialize-admin-and-link-account.md +235 -0
  30. package/playbooks/migrate-from-existing-auth.md +198 -0
  31. package/playbooks/protect-api-nextjs.md +451 -0
  32. package/playbooks/protect-routes-nextjs.md +544 -0
  33. package/playbooks/setup-forseti-e2ee.md +756 -0
  34. package/playbooks/setup-iga-admin-panel.md +344 -0
  35. package/playbooks/setup-server-delegation.md +142 -0
  36. package/playbooks/start-tidecloak-dev.md +130 -0
  37. package/playbooks/verify-jwt-server-side.md +439 -0
  38. package/prompts/add-admin-approval-flow.md +50 -0
  39. package/prompts/build-private-customer-portal.md +85 -0
  40. package/prompts/migrate-generic-auth-to-tide.md +67 -0
  41. package/prompts/secure-existing-app.md +80 -0
  42. package/reference-apps/INDEX.md +87 -0
  43. package/reference-apps/encrypted-communication/anti-patterns.md +123 -0
  44. package/reference-apps/encrypted-communication/bootstrap-sequence.md +152 -0
  45. package/reference-apps/encrypted-communication/manifest.yaml +100 -0
  46. package/reference-apps/encrypted-communication/role-policy-matrix.md +80 -0
  47. package/reference-apps/encrypted-communication/scenario.md +134 -0
  48. package/reference-apps/git-pr-signing-service/anti-patterns.md +61 -0
  49. package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +157 -0
  50. package/reference-apps/git-pr-signing-service/manifest.yaml +80 -0
  51. package/reference-apps/git-pr-signing-service/role-policy-matrix.md +99 -0
  52. package/reference-apps/git-pr-signing-service/scenario.md +169 -0
  53. package/reference-apps/iga-admin-governance/anti-patterns.md +133 -0
  54. package/reference-apps/iga-admin-governance/bootstrap-sequence.md +153 -0
  55. package/reference-apps/iga-admin-governance/manifest.yaml +87 -0
  56. package/reference-apps/iga-admin-governance/role-policy-matrix.md +67 -0
  57. package/reference-apps/iga-admin-governance/scenario.md +126 -0
  58. package/reference-apps/organisation-password-manager/anti-patterns.md +71 -0
  59. package/reference-apps/organisation-password-manager/bootstrap-sequence.md +127 -0
  60. package/reference-apps/organisation-password-manager/manifest.yaml +86 -0
  61. package/reference-apps/organisation-password-manager/role-policy-matrix.md +59 -0
  62. package/reference-apps/organisation-password-manager/scenario.md +107 -0
  63. package/reference-apps/policy-governed-signing/anti-patterns.md +67 -0
  64. package/reference-apps/policy-governed-signing/bootstrap-sequence.md +128 -0
  65. package/reference-apps/policy-governed-signing/manifest.yaml +78 -0
  66. package/reference-apps/policy-governed-signing/role-policy-matrix.md +62 -0
  67. package/reference-apps/policy-governed-signing/scenario.md +113 -0
  68. package/skills/tide-diagnostics/SKILL.md +99 -0
  69. package/skills/tide-integration/SKILL.md +136 -0
  70. package/skills/tide-learning-capture/SKILL.md +174 -0
  71. package/skills/tide-rbac-and-e2ee/SKILL.md +214 -0
  72. package/skills/tide-reviewer/SKILL.md +133 -0
  73. package/skills/tide-route-and-api-protection/SKILL.md +201 -0
  74. package/skills/tide-scenario-resolver/SKILL.md +81 -0
  75. package/skills/tide-setup/SKILL.md +218 -0
  76. package/skills/tide-solutions-architect/SKILL.md +130 -0
@@ -0,0 +1,235 @@
1
+ # Initialize Admin and Link Tide Account
2
+
3
+ Create the realm admin user, assign `tide-realm-admin`, generate the invite link, wait for account linking, approve user change requests, configure the IdP domain, and export the adapter JSON.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - After `bootstrap-realm-from-template` — realm exists with IGA enabled, client change requests approved
10
+ - Admin user does not yet exist in the realm
11
+
12
+ **Do not use** if the admin already has `tideUserKey` set. Check: `curl -H "Authorization: Bearer $TOKEN" "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" | jq -r '.[0].attributes.tideUserKey[0] // empty'`.
13
+
14
+ ---
15
+
16
+ ## Prerequisites
17
+
18
+ - TideCloak running with realm created and IGA enabled (playbook `bootstrap-realm-from-template` complete)
19
+ - `curl`, `jq` installed
20
+ - A browser to open the invite link (interactive step)
21
+
22
+ ---
23
+
24
+ ## Steps
25
+
26
+ ### Step 1: Create admin user
27
+
28
+ ```bash
29
+ TIDECLOAK_URL="${TIDECLOAK_URL:-http://localhost:8080}"
30
+ REALM_NAME="${REALM_NAME:-myapp}"
31
+ CLIENT_NAME="${CLIENT_NAME:-myclient}"
32
+ CLIENT_APP_URL="${CLIENT_APP_URL:-http://localhost:3000}"
33
+ ADMIN_EMAIL="${ADMIN_EMAIL:-admin@yourorg.com}"
34
+
35
+ get_token() {
36
+ curl -s -X POST "$TIDECLOAK_URL/realms/master/protocol/openid-connect/token" \
37
+ -H "Content-Type: application/x-www-form-urlencoded" \
38
+ -d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
39
+ | jq -r '.access_token'
40
+ }
41
+
42
+ TOKEN="$(get_token)"
43
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users" \
44
+ -H "Authorization: Bearer $TOKEN" \
45
+ -H "Content-Type: application/json" \
46
+ -d "{\"username\":\"admin\",\"email\":\"$ADMIN_EMAIL\",\"enabled\":true}"
47
+ ```
48
+
49
+ ### Step 1b: Approve user creation change request (if any)
50
+
51
+ With IGA enabled, user creation usually generates a change request. Approve before proceeding. **Note**: When the master admin token creates the first user, IGA may not generate a change request (the master admin bypasses realm-level IGA). The `approve_and_commit` function handles this gracefully — if no requests exist, it skips. VERIFIED (atproto-learnings L-02).
52
+
53
+ ```bash
54
+ sleep 2
55
+ approve_and_commit users
56
+ ```
57
+
58
+ (Use the `approve_and_commit` function from `bootstrap-realm-from-template`.)
59
+
60
+ **Failure if skipped**: User lookup in Step 2 returns `null`. Role lookup returns `{"error":"..."}` which gets passed as the role mapping body, producing `Invalid json representation for RoleRepresentation`.
61
+
62
+ ### Step 2: Assign tide-realm-admin role
63
+
64
+ `tide-realm-admin` is a **client role** on the `realm-management` client, not a realm role.
65
+
66
+ ```bash
67
+ TOKEN="$(get_token)"
68
+ USER_ID=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
69
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
70
+
71
+ if [ -z "$USER_ID" ] || [ "$USER_ID" = "null" ]; then
72
+ echo "ERROR: Could not find admin user. Was the user change request approved?"
73
+ exit 1
74
+ fi
75
+
76
+ TOKEN="$(get_token)"
77
+ CLIENT_UUID=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=realm-management" \
78
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
79
+
80
+ ROLE_JSON=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients/$CLIENT_UUID/roles/tide-realm-admin" \
81
+ -H "Authorization: Bearer $TOKEN")
82
+
83
+ # Validate we got a role, not an error
84
+ if echo "$ROLE_JSON" | jq -e '.error' > /dev/null 2>&1; then
85
+ echo "ERROR: tide-realm-admin role not found: $ROLE_JSON"
86
+ exit 1
87
+ fi
88
+
89
+ TOKEN="$(get_token)"
90
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users/$USER_ID/role-mappings/clients/$CLIENT_UUID" \
91
+ -H "Authorization: Bearer $TOKEN" \
92
+ -H "Content-Type: application/json" \
93
+ -d "[$ROLE_JSON]"
94
+ ```
95
+
96
+ ### Step 3: Generate invite link
97
+
98
+ Use the required-action-link endpoint. **Do not use `execute-actions-email`** — it fails on the dev image without SMTP configuration (`"Invalid sender address 'null'"`). The direct link method works without SMTP. VERIFIED (LEARNINGS-batch-008 L-05).
99
+
100
+ ```bash
101
+ TOKEN="$(get_token)"
102
+ INVITE_LINK=$(curl -s -X POST \
103
+ "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tideAdminResources/get-required-action-link?userId=$USER_ID&lifespan=43200" \
104
+ -H "Authorization: Bearer $TOKEN" \
105
+ -H "Content-Type: application/json" \
106
+ -d '["link-tide-account-action"]')
107
+
108
+ echo "====================================="
109
+ echo "Open this link in a browser to link your admin Tide account:"
110
+ echo "$INVITE_LINK"
111
+ echo "====================================="
112
+ ```
113
+
114
+ **This is an interactive step.** The admin must open the link in a browser and complete the Tide account linking flow. The script cannot automate this — it requires the user's browser.
115
+
116
+ ### Step 4: Wait for account linking
117
+
118
+ ```bash
119
+ echo "Waiting for admin to link Tide account..."
120
+ while true; do
121
+ TOKEN="$(get_token)"
122
+ KEY=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
123
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].attributes.tideUserKey[0] // empty')
124
+ [ -n "$KEY" ] && echo "Account linked." && break
125
+ sleep 5
126
+ done
127
+ ```
128
+
129
+ ### Step 5: Approve user change requests
130
+
131
+ ```bash
132
+ # Reuse approve_and_commit from bootstrap-realm-from-template
133
+ approve_and_commit() {
134
+ local TYPE=$1
135
+ TOKEN="$(get_token)"
136
+ local requests=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/$TYPE/requests" \
137
+ -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "[]")
138
+
139
+ echo "$requests" | jq -c '.[]' | while read -r req; do
140
+ local id=$(echo "$req" | jq -r .draftRecordId)
141
+ local cst=$(echo "$req" | jq -r .changeSetType)
142
+ local at=$(echo "$req" | jq -r .actionType)
143
+ local payload="{\"changeSetId\":\"$id\",\"changeSetType\":\"$cst\",\"actionType\":\"$at\"}"
144
+
145
+ TOKEN="$(get_token)"
146
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/sign" \
147
+ -H "Authorization: Bearer $TOKEN" \
148
+ -H "Content-Type: application/json" -d "$payload"
149
+
150
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/commit" \
151
+ -H "Authorization: Bearer $TOKEN" \
152
+ -H "Content-Type: application/json" -d "$payload"
153
+ done
154
+ }
155
+
156
+ approve_and_commit users
157
+ approve_and_commit roles
158
+ approve_and_commit clients
159
+ ```
160
+
161
+ **Sweep all types.** Account linking generates a user change request for the `tideUserKey` attribute write. The IGA toggle and setUpTideRealm may also have generated accumulated client change requests. Approve all three types here. VERIFIED (atproto-learnings L-06, L-07).
162
+
163
+ ### Step 6: Update CustomAdminUIDomain
164
+
165
+ Required for enclave approval popups to work from the app origin.
166
+
167
+ ```bash
168
+ TOKEN="$(get_token)"
169
+ INST=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
170
+ -H "Authorization: Bearer $TOKEN")
171
+ UPDATED=$(echo "$INST" | jq --arg d "$CLIENT_APP_URL" '.config.CustomAdminUIDomain=$d')
172
+ curl -s -X PUT "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
173
+ -H "Authorization: Bearer $TOKEN" \
174
+ -H "Content-Type: application/json" -d "$UPDATED"
175
+
176
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/sign-idp-settings" \
177
+ -H "Authorization: Bearer $TOKEN"
178
+ ```
179
+
180
+ ### Step 7: Export adapter JSON
181
+
182
+ ```bash
183
+ TOKEN="$(get_token)"
184
+ CLIENT_UUID=$(curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=$CLIENT_NAME" \
185
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
186
+
187
+ ADAPTER_OUTPUT="${ADAPTER_OUTPUT:-./data/tidecloak.json}"
188
+ curl -s "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
189
+ -H "Authorization: Bearer $TOKEN" > "$ADAPTER_OUTPUT"
190
+
191
+ echo "Adapter config saved to $ADAPTER_OUTPUT"
192
+ ```
193
+
194
+ Verify the export:
195
+ ```bash
196
+ node -e "const c=require('$ADAPTER_OUTPUT'); console.log('jwk:', !!c.jwk, 'vendorId:', !!c.vendorId, 'homeOrkUrl:', !!c.homeOrkUrl)"
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Verification
202
+
203
+ - [ ] Admin user exists with `tideUserKey` attribute set
204
+ - [ ] `tide-realm-admin` assigned (client role on `realm-management`)
205
+ - [ ] No pending user change requests remain
206
+ - [ ] CustomAdminUIDomain matches app origin
207
+ - [ ] Adapter JSON at `$ADAPTER_OUTPUT` contains `jwk`, `vendorId`, `homeOrkUrl`
208
+
209
+ ---
210
+
211
+ ## Common Failures
212
+
213
+ | Symptom | Cause | Fix |
214
+ |---------|-------|-----|
215
+ | Invite link returns 404 | `requiredActions` missing from realm template | Use full template with `link-tide-account-action` |
216
+ | "Could not find configuration for Required Action" | Tide env vars missing from container | Restart container with all required env vars |
217
+ | `tideUserKey` never appears | Admin didn't complete the linking flow | Open invite link in browser, complete the flow |
218
+ | Enclave popups don't work from app | CustomAdminUIDomain not set | Run Step 6 |
219
+ | Adapter JSON missing `jwk` | IGA not enabled | Run `toggle-iga` from `bootstrap-realm-from-template` Step 5 |
220
+
221
+ ---
222
+
223
+ ## Anti-Patterns
224
+
225
+ - **Do not** auto-approve IGA role assignments from the backend. `requiresApprovalPopup: true` means the browser enclave is required for VVK-signed UserContexts.
226
+ - **Do not** skip the invite link step. Account linking is interactive by design.
227
+ - **Do not** use `hasRealmRole("tide-realm-admin")` to check admin status. It is a client role on `realm-management`. Use `hasClientRole("tide-realm-admin", "realm-management")`.
228
+
229
+ ---
230
+
231
+ ## Next Step
232
+
233
+ Bootstrap is complete. Proceed to app integration:
234
+ - Fresh app → playbook `add-auth-nextjs-fresh`
235
+ - E2EE needed → playbook `configure-e2ee-roles-and-policies`
@@ -0,0 +1,198 @@
1
+ # Migrate from Existing Auth to TideCloak
2
+
3
+ Replace your current identity provider with TideCloak while keeping standard OIDC flows intact.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Migrating from vanilla Keycloak to TideCloak
10
+ - Migrating from Auth0, Okta, or any other OIDC-compliant provider
11
+ - Want distributed key signing, zero-knowledge passwords, or quorum governance without rewriting auth flows
12
+
13
+ **Do not use** if starting a greenfield project. See [add-auth-nextjs-fresh.md](add-auth-nextjs-fresh.md) instead.
14
+
15
+ ---
16
+
17
+ ## From Keycloak
18
+
19
+ Keycloak to TideCloak is the simplest path. TideCloak uses the same admin console, same OIDC endpoints, and same SDK surface.
20
+
21
+ ### Steps
22
+
23
+ 1. **Deploy TideCloak** in place of (or alongside) your Keycloak instance.
24
+ 2. **Download the new adapter config** (`tidecloak.json`) from the TideCloak admin console:
25
+ - Clients -> {your-client} -> Installation -> Keycloak OIDC JSON
26
+ 3. **Replace `keycloak.json` with `tidecloak.json`** in your application:
27
+ ```bash
28
+ # Backup existing config
29
+ cp data/keycloak.json data/keycloak.json.bak
30
+
31
+ # Replace with TideCloak adapter
32
+ cp tidecloak.json data/keycloak.json
33
+ # Or rename to tidecloak.json and update your config loader path
34
+ ```
35
+ 4. **No code changes needed.** Same SDK, same OIDC protocol. The application does not need to know it is talking to TideCloak instead of Keycloak.
36
+
37
+ ### What to verify after swap
38
+
39
+ ```bash
40
+ # Confirm discovery endpoint responds
41
+ curl -s http://localhost:8080/realms/{realm}/.well-known/openid-configuration | jq .issuer
42
+
43
+ # Confirm JWKS endpoint responds
44
+ curl -s http://localhost:8080/realms/{realm}/protocol/openid-connect/certs | jq .keys[0].kty
45
+ ```
46
+
47
+ ---
48
+
49
+ ## From Other OIDC Providers (Auth0, Okta, etc.)
50
+
51
+ Any application using standard OIDC can point to TideCloak by updating configuration values. No TideCloak-specific SDK is required for basic authentication.
52
+
53
+ ### Steps
54
+
55
+ 1. **Deploy TideCloak.**
56
+ 2. **Create a realm and client** in TideCloak matching your existing client configuration (redirect URIs, scopes, grant types).
57
+ 3. **Update your application's OIDC configuration:**
58
+
59
+ | Setting | Old value (example: Auth0) | New value (TideCloak) |
60
+ |---------|---------------------------|----------------------|
61
+ | Discovery endpoint | `https://tenant.auth0.com/.well-known/openid-configuration` | `http://localhost:8080/realms/{realm}/.well-known/openid-configuration` |
62
+ | Issuer URL | `https://tenant.auth0.com/` | `http://localhost:8080/realms/{realm}` |
63
+ | Client ID | Your Auth0 client ID | Your TideCloak client ID |
64
+ | Client secret (if confidential) | Your Auth0 client secret | Your TideCloak client secret |
65
+
66
+ 4. **Standard OIDC -- no TideCloak SDK required for basic auth.** Your existing OIDC library (e.g., `openid-client`, `next-auth` with generic provider, `passport-openidconnect`) works without changes beyond the config values above.
67
+ 5. **Add TideCloak SDK only if you need Tide-specific features:**
68
+ ```bash
69
+ npm install @tidecloak/js
70
+ # or
71
+ npm install @tidecloak/react
72
+ ```
73
+ The SDK is required for E2EE features (`doEncrypt()` / `doDecrypt()`) and direct enclave interactions. It is not required for login, logout, or token verification.
74
+
75
+ ---
76
+
77
+ ## What Stays the Same
78
+
79
+ These are standard OIDC behaviors. TideCloak does not change them:
80
+
81
+ - **OIDC/OAuth 2.0 flows** -- Authorization Code, PKCE, Client Credentials all work identically.
82
+ - **JWT format and claims structure** -- Access tokens are standard JWTs. No proprietary wrapper.
83
+ - **JWKS verification endpoint** -- `GET /realms/{realm}/protocol/openid-connect/certs` returns standard JWK set. Existing JWT verification code works without modification.
84
+ - **Role claim paths** -- `realm_access.roles` for realm roles, `resource_access.{client}.roles` for client roles. Same as Keycloak.
85
+ - **Redirect URI patterns** -- Same configuration, same behavior.
86
+
87
+ ---
88
+
89
+ ## What You Gain
90
+
91
+ These improvements are invisible to the application. No code changes required to benefit:
92
+
93
+ | Feature | What it means |
94
+ |---------|--------------|
95
+ | **VVK threshold signing** | JWTs are signed by a distributed Vendor Verification Key. No single signing key exists to steal. Compromise of one node does not compromise tokens. |
96
+ | **Zero-knowledge passwords** | User passwords are never stored as hashes. The server never sees the plaintext password. Credential database breaches yield nothing usable. |
97
+ | **Quorum governance** | Admin changes (role assignments, policy updates) require multiple admin approvals before taking effect. Single compromised admin cannot unilaterally escalate privileges. |
98
+ | **Optional E2EE** | `doEncrypt()` / `doDecrypt()` available via TideCloak SDK for end-to-end encrypted data fields. Requires `@tidecloak/js` or `@tidecloak/react`. |
99
+
100
+ ---
101
+
102
+ ## Verification Checklist
103
+
104
+ ### Discovery and Tokens
105
+
106
+ - [ ] `GET /realms/{realm}/.well-known/openid-configuration` returns valid JSON with correct `issuer`
107
+ - [ ] `GET /realms/{realm}/protocol/openid-connect/certs` returns JWKS with at least one key
108
+ - [ ] Application obtains access token via Authorization Code flow
109
+ - [ ] Access token decodes to standard JWT with expected claims (`sub`, `realm_access`, `iss`)
110
+
111
+ ### Login and Logout
112
+
113
+ - [ ] Login redirects to TideCloak login page (not old provider)
114
+ - [ ] Successful login redirects back to application with valid tokens
115
+ - [ ] Logout clears session and redirects correctly
116
+ - [ ] Silent token refresh works (no forced re-login after token expiry)
117
+
118
+ ### JWT Verification
119
+
120
+ - [ ] Existing server-side JWT verification code accepts TideCloak tokens without modification
121
+ - [ ] `iss` claim matches the new TideCloak issuer URL
122
+ - [ ] Role claims at `realm_access.roles` are populated correctly
123
+
124
+ ### No Residual Old Auth
125
+
126
+ - [ ] No requests to old provider endpoints in browser network tab
127
+ - [ ] No old provider cookies or tokens in browser storage
128
+ - [ ] No old provider SDKs still loaded in the application bundle
129
+
130
+ ---
131
+
132
+ ## Common Failures
133
+
134
+ ### Issuer Mismatch
135
+
136
+ **Symptom**: JWT verification fails. Error message mentions issuer validation.
137
+
138
+ **Cause**: Application still expects the old provider's issuer URL (e.g., `https://tenant.auth0.com/`) but receives TideCloak's issuer (`http://localhost:8080/realms/myrealm`).
139
+
140
+ **Fix**: Update the expected issuer in your JWT verification configuration to match TideCloak's issuer URL exactly. Check trailing slashes.
141
+
142
+ ---
143
+
144
+ ### CORS Errors After Switch
145
+
146
+ **Symptom**: Browser console shows CORS errors on token or JWKS requests.
147
+
148
+ **Cause**: TideCloak's Web Origins configuration does not include your application's origin.
149
+
150
+ **Fix**: In TideCloak admin console: Clients -> {your-client} -> Settings -> Web Origins. Add your application origin (e.g., `http://localhost:3000`).
151
+
152
+ ---
153
+
154
+ ### Redirect URI Mismatch
155
+
156
+ **Symptom**: Login fails with "Invalid redirect_uri" error on TideCloak login page.
157
+
158
+ **Cause**: The redirect URI your application sends does not match any URI registered in TideCloak.
159
+
160
+ **Fix**: In TideCloak admin console: Clients -> {your-client} -> Settings -> Valid Redirect URIs. Add all URIs your application uses (including `http://localhost:*` for development).
161
+
162
+ ---
163
+
164
+ ### Old Provider Tokens Cached
165
+
166
+ **Symptom**: Application appears logged in but API calls fail with 401. Or infinite redirect loop.
167
+
168
+ **Cause**: Browser still holds tokens or cookies from the old provider.
169
+
170
+ **Fix**: Clear site data in browser DevTools (Application -> Storage -> Clear site data). Test in incognito window for a clean session.
171
+
172
+ ---
173
+
174
+ ### Client Secret Not Configured (Confidential Clients)
175
+
176
+ **Symptom**: Token exchange fails with 401 from TideCloak token endpoint.
177
+
178
+ **Cause**: Confidential client in TideCloak does not have the client secret configured, or the application is sending the old provider's secret.
179
+
180
+ **Fix**: In TideCloak admin console: Clients -> {your-client} -> Credentials. Copy the secret and update your application's configuration.
181
+
182
+ ---
183
+
184
+ ### Role Claims Missing or Different Path
185
+
186
+ **Symptom**: Application cannot find user roles after migration.
187
+
188
+ **Cause**: Old provider used a custom claim path (e.g., Auth0 namespaced claims like `https://myapp.com/roles`). TideCloak uses standard Keycloak paths.
189
+
190
+ **Fix**: Update role extraction code to read from `realm_access.roles` (realm roles) or `resource_access.{client}.roles` (client roles). If your application hardcodes a custom claim namespace, replace it.
191
+
192
+ ---
193
+
194
+ ## References
195
+
196
+ - Source: SDK documentation, operational exemplars
197
+ - Keycloak migration: same admin console, same OIDC endpoints, config swap only
198
+ - Generic OIDC migration: config-only change, no SDK required for basic auth