@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,792 @@
|
|
|
1
|
+
# Deploy TideCloak with Docker and Auto-Initialize
|
|
2
|
+
|
|
3
|
+
Deploy TideCloak in Docker, auto-initialize a realm with Tide licensing, IGA, admin creation, and adapter config export.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- Setting up TideCloak for the first time on a development or staging machine
|
|
10
|
+
- Automating realm creation instead of clicking through the Admin Console
|
|
11
|
+
- Need a repeatable, scriptable TideCloak deployment for CI or onboarding
|
|
12
|
+
- Rebuilding a TideCloak instance from scratch after a wipe
|
|
13
|
+
|
|
14
|
+
**Do not use** if:
|
|
15
|
+
- You already have a running TideCloak with a configured realm (use Admin Console or API directly)
|
|
16
|
+
- You are deploying to production with an external database (this playbook covers H2-backed dev/staging only)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
### Tools
|
|
23
|
+
|
|
24
|
+
- Docker installed and running (`docker info` succeeds)
|
|
25
|
+
- `curl` installed
|
|
26
|
+
- `jq` installed
|
|
27
|
+
- `lsof` installed (for port conflict detection)
|
|
28
|
+
- `sudo` access (for data directory permissions)
|
|
29
|
+
|
|
30
|
+
### Network
|
|
31
|
+
|
|
32
|
+
- Port 8080 available on the host (or change mapping)
|
|
33
|
+
- Internet access to pull `tideorg/tidecloak-dev` or `tideorg/tidecloak-stg-dev` images
|
|
34
|
+
- Internet access to reach `https://sork1.tideprotocol.com` (Tide ORK network) if using staging image
|
|
35
|
+
|
|
36
|
+
### Decision: Which Image
|
|
37
|
+
|
|
38
|
+
| Image | Use Case | Database | Tide Network |
|
|
39
|
+
|-------|----------|----------|--------------|
|
|
40
|
+
| `tideorg/tidecloak-dev:latest` | Local development, single node, no Tide network needed | H2 (embedded) | Not connected |
|
|
41
|
+
| `tideorg/tidecloak-stg-dev:latest` | Staging, connects to Tide ORK network, IGA fully functional | H2 (embedded) | Connected via ORK env vars |
|
|
42
|
+
|
|
43
|
+
Use `tidecloak-dev` for quick local work. Use `tidecloak-stg-dev` when you need the full Tide protocol (licensing, IGA approval flows, admin invite links).
|
|
44
|
+
|
|
45
|
+
**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.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Environment Variables
|
|
50
|
+
|
|
51
|
+
| Variable | Purpose | Example | Required For |
|
|
52
|
+
|----------|---------|---------|--------------|
|
|
53
|
+
| `KC_BOOTSTRAP_ADMIN_USERNAME` | Initial admin user | `admin` | Both images |
|
|
54
|
+
| `KC_BOOTSTRAP_ADMIN_PASSWORD` | Initial admin password | `password` | Both images |
|
|
55
|
+
| `KC_HOSTNAME` | Public-facing URL | `https://auth.myapp.com` | Staging only |
|
|
56
|
+
| `SYSTEM_HOME_ORK` | Tide network entry point | `https://sork1.tideprotocol.com` | Staging only |
|
|
57
|
+
| `USER_HOME_ORK` | User-facing ORK URL | `https://sork1.tideprotocol.com` | Staging only |
|
|
58
|
+
| `THRESHOLD_T` | Signing threshold | `3` | Staging only |
|
|
59
|
+
| `THRESHOLD_N` | Total ORK nodes | `5` | Staging only |
|
|
60
|
+
| `PAYER_PUBLIC` | Payer ORK public key | hex string | Staging only |
|
|
61
|
+
| `KC_PROXY_HEADERS` | Trust forwarded headers from reverse proxy | `xforwarded` | Behind reverse proxy only |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Exact Step Sequence
|
|
66
|
+
|
|
67
|
+
### Step 1: Start TideCloak (Development)
|
|
68
|
+
|
|
69
|
+
For local development without Tide network connection:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
mkdir -p ./data
|
|
73
|
+
sudo docker run -d \
|
|
74
|
+
--name tidecloak \
|
|
75
|
+
-v ./data:/opt/keycloak/data/h2 \
|
|
76
|
+
-p 8080:8080 \
|
|
77
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
78
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
|
|
79
|
+
tideorg/tidecloak-dev:latest
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### Step 1 (Alternative): Start TideCloak (Staging)
|
|
85
|
+
|
|
86
|
+
For staging with full Tide protocol:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
mkdir -p ./data
|
|
90
|
+
docker run -d \
|
|
91
|
+
--name tidecloak \
|
|
92
|
+
-v ./data:/opt/keycloak/data/h2 \
|
|
93
|
+
-p 8080:8080 \
|
|
94
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
95
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
|
|
96
|
+
-e KC_HOSTNAME=https://auth.myapp.com \
|
|
97
|
+
-e SYSTEM_HOME_ORK=https://sork1.tideprotocol.com \
|
|
98
|
+
-e USER_HOME_ORK=https://sork1.tideprotocol.com \
|
|
99
|
+
-e THRESHOLD_T=3 \
|
|
100
|
+
-e THRESHOLD_N=5 \
|
|
101
|
+
-e PAYER_PUBLIC=20000011d6a0e8212d682657147d864b82d10e92776c15ead43dcfdc100ebf4dcfe6a8 \
|
|
102
|
+
tideorg/tidecloak-stg-dev:latest
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Important:** Always mount a dedicated `data/` subdirectory, not the project root (`.`). Mounting the project root causes H2 database permission errors (`AccessDeniedException`) because the container writes DB files as a different user than your project files.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Step 2: Create the realm.json Template
|
|
110
|
+
|
|
111
|
+
Create a file named `realm.json` in your working directory. This template is used by the auto-initialization script.
|
|
112
|
+
|
|
113
|
+
**CRITICAL:** This template must be used exactly as shown. Do not create a minimal version. Every section is required for Tide to initialize correctly. Missing `requiredActions` causes invite link failures. Missing `authenticationFlows` causes login failures. Missing `protocolMappers` causes missing claims in JWTs.
|
|
114
|
+
|
|
115
|
+
Replace `REALM_NAME` and `CLIENT_NAME` with your values. Replace `CLIENT_APP_URL` with your app's URL. The init script performs these replacements automatically via `sed`.
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"realm": "REALM_NAME",
|
|
120
|
+
"accessTokenLifespan": 600,
|
|
121
|
+
"enabled": true,
|
|
122
|
+
"sslRequired": "external",
|
|
123
|
+
"registrationAllowed": false,
|
|
124
|
+
"duplicateEmailsAllowed": true,
|
|
125
|
+
"roles": {
|
|
126
|
+
"realm": [
|
|
127
|
+
{ "name": "appUser", "description": "Standard application user" },
|
|
128
|
+
{ "name": "_tide_enabled", "description": "Represents a tide user thats allowed perform actions on tide" },
|
|
129
|
+
{
|
|
130
|
+
"name": "default-roles-REALM_NAME",
|
|
131
|
+
"description": "${role_default-roles}",
|
|
132
|
+
"composite": true,
|
|
133
|
+
"composites": { "realm": ["_tide_enabled", "appUser"] }
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"client": { "CLIENT_NAME": [] }
|
|
137
|
+
},
|
|
138
|
+
"defaultRole": {
|
|
139
|
+
"name": "default-roles-REALM_NAME",
|
|
140
|
+
"description": "${role_default-roles}",
|
|
141
|
+
"composite": true,
|
|
142
|
+
"clientRole": false
|
|
143
|
+
},
|
|
144
|
+
"clients": [
|
|
145
|
+
{
|
|
146
|
+
"clientId": "CLIENT_NAME",
|
|
147
|
+
"enabled": true,
|
|
148
|
+
"redirectUris": [
|
|
149
|
+
"CLIENT_APP_URL",
|
|
150
|
+
"CLIENT_APP_URL/*",
|
|
151
|
+
"CLIENT_APP_URL/silent-check-sso.html",
|
|
152
|
+
"CLIENT_APP_URL/auth/redirect"
|
|
153
|
+
],
|
|
154
|
+
"webOrigins": ["CLIENT_APP_URL"],
|
|
155
|
+
"standardFlowEnabled": true,
|
|
156
|
+
"implicitFlowEnabled": false,
|
|
157
|
+
"publicClient": true,
|
|
158
|
+
"fullScopeAllowed": true,
|
|
159
|
+
"protocolMappers": [
|
|
160
|
+
{
|
|
161
|
+
"name": "Tide User Key",
|
|
162
|
+
"protocol": "openid-connect",
|
|
163
|
+
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
|
164
|
+
"consentRequired": false,
|
|
165
|
+
"config": {
|
|
166
|
+
"introspection.token.claim": "true",
|
|
167
|
+
"userinfo.token.claim": "true",
|
|
168
|
+
"user.attribute": "tideUserKey",
|
|
169
|
+
"lightweight.claim": "true",
|
|
170
|
+
"id.token.claim": "true",
|
|
171
|
+
"access.token.claim": "true",
|
|
172
|
+
"claim.name": "tideuserkey",
|
|
173
|
+
"jsonType.label": "String"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "Tide IGA Role Mapper",
|
|
178
|
+
"protocol": "openid-connect",
|
|
179
|
+
"protocolMapper": "tide-roles-mapper",
|
|
180
|
+
"consentRequired": false,
|
|
181
|
+
"config": {
|
|
182
|
+
"lightweight.claim": "true",
|
|
183
|
+
"access.token.claim": "true"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "Tide vuid",
|
|
188
|
+
"protocol": "openid-connect",
|
|
189
|
+
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
|
190
|
+
"consentRequired": false,
|
|
191
|
+
"config": {
|
|
192
|
+
"introspection.token.claim": "true",
|
|
193
|
+
"userinfo.token.claim": "true",
|
|
194
|
+
"user.attribute": "vuid",
|
|
195
|
+
"lightweight.claim": "true",
|
|
196
|
+
"id.token.claim": "true",
|
|
197
|
+
"access.token.claim": "true",
|
|
198
|
+
"claim.name": "vuid",
|
|
199
|
+
"jsonType.label": "String"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
"components": {
|
|
206
|
+
"org.keycloak.userprofile.UserProfileProvider": [
|
|
207
|
+
{
|
|
208
|
+
"providerId": "declarative-user-profile",
|
|
209
|
+
"config": {
|
|
210
|
+
"kc.user.profile.config": [
|
|
211
|
+
"{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}"
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
"authenticationFlows": [
|
|
218
|
+
{
|
|
219
|
+
"alias": "tidebrowser",
|
|
220
|
+
"providerId": "basic-flow",
|
|
221
|
+
"topLevel": true,
|
|
222
|
+
"authenticationExecutions": [
|
|
223
|
+
{
|
|
224
|
+
"authenticator": "auth-cookie",
|
|
225
|
+
"authenticatorFlow": false,
|
|
226
|
+
"requirement": "ALTERNATIVE",
|
|
227
|
+
"priority": 10,
|
|
228
|
+
"userSetupAllowed": false
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"authenticatorConfig": "tide browser",
|
|
232
|
+
"authenticator": "identity-provider-redirector",
|
|
233
|
+
"authenticatorFlow": false,
|
|
234
|
+
"requirement": "ALTERNATIVE",
|
|
235
|
+
"priority": 25,
|
|
236
|
+
"userSetupAllowed": false
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
"authenticatorConfig": [
|
|
242
|
+
{
|
|
243
|
+
"alias": "tide browser",
|
|
244
|
+
"config": { "defaultProvider": "tide" }
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
"browserFlow": "tidebrowser",
|
|
248
|
+
"requiredActions": [
|
|
249
|
+
{
|
|
250
|
+
"alias": "link-tide-account-action",
|
|
251
|
+
"name": "Link Tide Account",
|
|
252
|
+
"providerId": "link-tide-account-action",
|
|
253
|
+
"enabled": true
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"alias": "UPDATE_PASSWORD",
|
|
257
|
+
"name": "Update Password",
|
|
258
|
+
"providerId": "UPDATE_PASSWORD",
|
|
259
|
+
"enabled": false
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### Step 3: Run the Auto-Initialization Script
|
|
268
|
+
|
|
269
|
+
This script automates the full realm setup: container launch, realm creation, Tide licensing, IGA enablement, admin user creation, invite link generation, change request approval, IdP configuration, and adapter config export.
|
|
270
|
+
|
|
271
|
+
**Initialization sequence order (never skip or reorder):**
|
|
272
|
+
|
|
273
|
+
1. Clean up previous state
|
|
274
|
+
2. Start container
|
|
275
|
+
3. Wait for TideCloak readiness
|
|
276
|
+
4. Get admin token
|
|
277
|
+
5. Create realm from template
|
|
278
|
+
6. Initialize Tide realm + enable IGA
|
|
279
|
+
7. Approve client change requests
|
|
280
|
+
8. Create admin user
|
|
281
|
+
9. Approve user creation change request (user is not queryable until committed)
|
|
282
|
+
10. Assign `tide-realm-admin` role (validate role lookup succeeds)
|
|
283
|
+
11. Generate invite link (admin links their Tide account)
|
|
284
|
+
12. Wait for admin to link their Tide account
|
|
285
|
+
13. Approve role assignment change requests
|
|
286
|
+
14. Update CustomAdminUIDomain for enclave approval popups
|
|
287
|
+
15. Download adapter config
|
|
288
|
+
|
|
289
|
+
Save the following as `init-tidecloak.sh`:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
#!/bin/bash
|
|
293
|
+
# Configuration
|
|
294
|
+
TIDECLOAK_URL="${TIDECLOAK_LOCAL_URL:-http://localhost:8080}"
|
|
295
|
+
CLIENT_APP_URL="${CLIENT_APP_URL:-http://localhost:3000}"
|
|
296
|
+
REALM_NAME="${NEW_REALM_NAME:-myapp}"
|
|
297
|
+
CLIENT_NAME="${CLIENT_NAME:-myclient}"
|
|
298
|
+
ADAPTER_OUTPUT="./data/tidecloak.json"
|
|
299
|
+
|
|
300
|
+
# 0. Clean up any previous state
|
|
301
|
+
# - Stop and remove existing container (if any)
|
|
302
|
+
# - Remove stale H2 database files (cause "Could not open file" errors)
|
|
303
|
+
# - Fix directory permissions (container runs as UID 1000)
|
|
304
|
+
# - Handle port conflicts
|
|
305
|
+
docker stop tidecloak 2>/dev/null
|
|
306
|
+
docker rm tidecloak 2>/dev/null
|
|
307
|
+
mkdir -p ./data
|
|
308
|
+
sudo rm -f ./data/keycloakdb* 2>/dev/null
|
|
309
|
+
sudo chown -R 1000:1000 ./data
|
|
310
|
+
|
|
311
|
+
# Check if port 8080 is already in use
|
|
312
|
+
if lsof -i :8080 >/dev/null 2>&1; then
|
|
313
|
+
echo "ERROR: Port 8080 is already in use. Stop the existing service or change the port."
|
|
314
|
+
exit 1
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# Dev image (default): no ORK/threshold env vars needed.
|
|
318
|
+
# For staging image (tidecloak-stg-dev), add: KC_HOSTNAME, SYSTEM_HOME_ORK,
|
|
319
|
+
# USER_HOME_ORK, THRESHOLD_T, THRESHOLD_N, PAYER_PUBLIC.
|
|
320
|
+
sudo docker run -d --name tidecloak \
|
|
321
|
+
-v "$(pwd)/data:/opt/keycloak/data/h2" \
|
|
322
|
+
-p 8080:8080 \
|
|
323
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
324
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
|
|
325
|
+
tideorg/tidecloak-dev:latest
|
|
326
|
+
|
|
327
|
+
if [ $? -ne 0 ]; then
|
|
328
|
+
echo "ERROR: Docker failed to start. Check 'docker logs tidecloak' for details."
|
|
329
|
+
exit 1
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
# 1. Wait for TideCloak to be ready
|
|
333
|
+
for i in {1..15}; do
|
|
334
|
+
curl -s -f "$TIDECLOAK_URL" > /dev/null 2>&1 && break
|
|
335
|
+
echo "Waiting for TideCloak (attempt $i/15)..."
|
|
336
|
+
sleep 5
|
|
337
|
+
done
|
|
338
|
+
|
|
339
|
+
# 2. Get admin token
|
|
340
|
+
get_token() {
|
|
341
|
+
curl -s -X POST "$TIDECLOAK_URL/realms/master/protocol/openid-connect/token" \
|
|
342
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
343
|
+
-d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
|
|
344
|
+
| jq -r '.access_token'
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# 3. Create realm from template
|
|
348
|
+
# Replace placeholders in realm.json with actual values
|
|
349
|
+
TMP_REALM="$(mktemp)"
|
|
350
|
+
cp realm.json "$TMP_REALM"
|
|
351
|
+
sed -i "s|REALM_NAME|$REALM_NAME|g" "$TMP_REALM"
|
|
352
|
+
sed -i "s|CLIENT_NAME|$CLIENT_NAME|g" "$TMP_REALM"
|
|
353
|
+
sed -i "s|CLIENT_APP_URL|$CLIENT_APP_URL|g" "$TMP_REALM"
|
|
354
|
+
|
|
355
|
+
TOKEN="$(get_token)"
|
|
356
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms" \
|
|
357
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
358
|
+
-H "Content-Type: application/json" \
|
|
359
|
+
--data-binary @"$TMP_REALM"
|
|
360
|
+
rm -f "$TMP_REALM"
|
|
361
|
+
|
|
362
|
+
# 4. Initialize Tide realm + enable IGA
|
|
363
|
+
TOKEN="$(get_token)"
|
|
364
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/setUpTideRealm" \
|
|
365
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
366
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
367
|
+
--data-urlencode "email=admin@yourorg.com" \
|
|
368
|
+
--data-urlencode "isRagnarokEnabled=true"
|
|
369
|
+
|
|
370
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/toggle-iga" \
|
|
371
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
372
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
373
|
+
--data-urlencode "isIGAEnabled=true"
|
|
374
|
+
|
|
375
|
+
# 5. Auto-approve client change requests (created during realm setup)
|
|
376
|
+
approve_and_commit() {
|
|
377
|
+
local TYPE=$1
|
|
378
|
+
TOKEN="$(get_token)"
|
|
379
|
+
local requests=$(curl -s -X GET \
|
|
380
|
+
"$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/$TYPE/requests" \
|
|
381
|
+
-H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "[]")
|
|
382
|
+
|
|
383
|
+
echo "$requests" | jq -c '.[]' | while read -r req; do
|
|
384
|
+
local payload=$(jq -n \
|
|
385
|
+
--arg id "$(echo "$req" | jq -r .draftRecordId)" \
|
|
386
|
+
--arg cst "$(echo "$req" | jq -r .changeSetType)" \
|
|
387
|
+
--arg at "$(echo "$req" | jq -r .actionType)" \
|
|
388
|
+
'{changeSetId:$id,changeSetType:$cst,actionType:$at}')
|
|
389
|
+
|
|
390
|
+
# Sign then commit
|
|
391
|
+
TOKEN="$(get_token)"
|
|
392
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/sign" \
|
|
393
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
394
|
+
-H "Content-Type: application/json" -d "$payload"
|
|
395
|
+
|
|
396
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/commit" \
|
|
397
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
398
|
+
-H "Content-Type: application/json" -d "$payload"
|
|
399
|
+
done
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
approve_and_commit clients
|
|
403
|
+
|
|
404
|
+
# 6. Create admin user
|
|
405
|
+
TOKEN="$(get_token)"
|
|
406
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users" \
|
|
407
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
408
|
+
-H "Content-Type: application/json" \
|
|
409
|
+
-d '{"username":"admin","email":"admin@yourorg.com","enabled":true}'
|
|
410
|
+
|
|
411
|
+
# 6b. Approve user creation (user is not queryable until committed with IGA enabled)
|
|
412
|
+
sleep 2
|
|
413
|
+
approve_and_commit users
|
|
414
|
+
|
|
415
|
+
# 6c. Assign tide-realm-admin role
|
|
416
|
+
TOKEN="$(get_token)"
|
|
417
|
+
USER_ID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
|
|
418
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
|
|
419
|
+
|
|
420
|
+
if [ -z "$USER_ID" ] || [ "$USER_ID" = "null" ]; then
|
|
421
|
+
echo "ERROR: Could not find admin user. Was the user change request approved?"
|
|
422
|
+
exit 1
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=realm-management" \
|
|
426
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
|
|
427
|
+
|
|
428
|
+
ROLE_JSON=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients/$CLIENT_UUID/roles/tide-realm-admin" \
|
|
429
|
+
-H "Authorization: Bearer $TOKEN")
|
|
430
|
+
|
|
431
|
+
if echo "$ROLE_JSON" | jq -e '.error' > /dev/null 2>&1; then
|
|
432
|
+
echo "ERROR: tide-realm-admin role not found: $ROLE_JSON"
|
|
433
|
+
exit 1
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
TOKEN="$(get_token)"
|
|
437
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users/$USER_ID/role-mappings/clients/$CLIENT_UUID" \
|
|
438
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
439
|
+
-H "Content-Type: application/json" \
|
|
440
|
+
-d "[$ROLE_JSON]"
|
|
441
|
+
|
|
442
|
+
# 7. Generate invite link for admin to link their Tide account
|
|
443
|
+
TOKEN="$(get_token)"
|
|
444
|
+
INVITE_LINK=$(curl -s -X POST \
|
|
445
|
+
"$TIDECLOAK_URL/admin/realms/$REALM_NAME/tideAdminResources/get-required-action-link?userId=$USER_ID&lifespan=43200" \
|
|
446
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
447
|
+
-H "Content-Type: application/json" \
|
|
448
|
+
-d '["link-tide-account-action"]')
|
|
449
|
+
|
|
450
|
+
echo "Open this link to link your admin Tide account:"
|
|
451
|
+
echo "$INVITE_LINK"
|
|
452
|
+
|
|
453
|
+
# 8. Wait for admin to link their Tide account
|
|
454
|
+
while true; do
|
|
455
|
+
TOKEN="$(get_token)"
|
|
456
|
+
KEY=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
|
|
457
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].attributes.tideUserKey[0] // empty')
|
|
458
|
+
[ -n "$KEY" ] && break
|
|
459
|
+
sleep 5
|
|
460
|
+
done
|
|
461
|
+
|
|
462
|
+
# 9. Approve role assignment change requests
|
|
463
|
+
sleep 2
|
|
464
|
+
approve_and_commit users
|
|
465
|
+
approve_and_commit roles
|
|
466
|
+
|
|
467
|
+
# 10. Update CustomAdminUIDomain for enclave approval popups
|
|
468
|
+
# CustomAdminUIDomain is OPTIONAL and APP-SPECIFIC: only needed when a separate
|
|
469
|
+
# application (not the built-in Admin Console) hosts the change-set approval UI.
|
|
470
|
+
# sign-idp-settings is ALWAYS required after any Tide IDP config change.
|
|
471
|
+
TOKEN="$(get_token)"
|
|
472
|
+
INST=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
|
|
473
|
+
-H "Authorization: Bearer $TOKEN")
|
|
474
|
+
UPDATED=$(echo "$INST" | jq --arg d "$CLIENT_APP_URL" '.config.CustomAdminUIDomain=$d')
|
|
475
|
+
curl -s -X PUT "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
|
|
476
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
477
|
+
-H "Content-Type: application/json" -d "$UPDATED"
|
|
478
|
+
curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/sign-idp-settings" \
|
|
479
|
+
-H "Authorization: Bearer $TOKEN"
|
|
480
|
+
|
|
481
|
+
# 11. Download adapter config
|
|
482
|
+
TOKEN="$(get_token)"
|
|
483
|
+
CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=$CLIENT_NAME" \
|
|
484
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
|
|
485
|
+
curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
|
|
486
|
+
-H "Authorization: Bearer $TOKEN" > "$ADAPTER_OUTPUT"
|
|
487
|
+
|
|
488
|
+
echo "Done! Adapter config saved to $ADAPTER_OUTPUT"
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Make it executable and run:
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
chmod +x init-tidecloak.sh
|
|
495
|
+
./init-tidecloak.sh
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Override defaults with environment variables:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
TIDECLOAK_LOCAL_URL=http://localhost:8080 \
|
|
502
|
+
CLIENT_APP_URL=http://localhost:3000 \
|
|
503
|
+
NEW_REALM_NAME=myapp \
|
|
504
|
+
CLIENT_NAME=myclient \
|
|
505
|
+
./init-tidecloak.sh
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
### Step 4: Download Adapter Config
|
|
511
|
+
|
|
512
|
+
The init script saves the adapter config to `./data/tidecloak.json` automatically.
|
|
513
|
+
|
|
514
|
+
If you need to download it manually after initialization:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
TIDECLOAK_URL="http://localhost:8080"
|
|
518
|
+
REALM_NAME="myapp"
|
|
519
|
+
CLIENT_NAME="myclient"
|
|
520
|
+
|
|
521
|
+
TOKEN=$(curl -s -X POST "$TIDECLOAK_URL/realms/master/protocol/openid-connect/token" \
|
|
522
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
523
|
+
-d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
|
|
524
|
+
| jq -r '.access_token')
|
|
525
|
+
|
|
526
|
+
CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=$CLIENT_NAME" \
|
|
527
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
|
|
528
|
+
|
|
529
|
+
curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
|
|
530
|
+
-H "Authorization: Bearer $TOKEN" > ./data/tidecloak.json
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Key Initialization Endpoints
|
|
536
|
+
|
|
537
|
+
| Endpoint | Purpose |
|
|
538
|
+
|----------|---------|
|
|
539
|
+
| `POST /admin/realms` | Create realm from JSON template |
|
|
540
|
+
| `POST /admin/realms/{realm}/vendorResources/setUpTideRealm` | Initialize Tide licensing + VRK |
|
|
541
|
+
| `POST /admin/realms/{realm}/tide-admin/toggle-iga` | Enable IGA governance |
|
|
542
|
+
| `POST /admin/realms/{realm}/tide-admin/change-set/sign` | Approve a change request |
|
|
543
|
+
| `POST /admin/realms/{realm}/tide-admin/change-set/commit` | Commit an approved change |
|
|
544
|
+
| `POST /admin/realms/{realm}/tideAdminResources/get-required-action-link` | Generate admin invite link |
|
|
545
|
+
| `GET /admin/realms/{realm}/vendorResources/get-installations-provider` | Download adapter config |
|
|
546
|
+
| `POST /admin/realms/{realm}/vendorResources/sign-idp-settings` | Sign IdP settings with VRK. **ALWAYS required** after any Tide IDP config change (logo, background, backup toggle, custom admin domain, or client origin changes). Without it, the enclave rejects settings as unsigned. Requires active VRK/license. |
|
|
547
|
+
| `POST /admin/realms/{realm}/tide-idp-admin-resources/images/upload` | Upload branding (logo/background) |
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Verification Checklist
|
|
552
|
+
|
|
553
|
+
### Container Running
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
docker ps --filter name=tidecloak
|
|
557
|
+
# Should show running container
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### TideCloak Responds
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
curl -s -f http://localhost:8080
|
|
564
|
+
# Should return HTML (Keycloak welcome page)
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Admin Token Works
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
|
|
571
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
572
|
+
-d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
|
|
573
|
+
| jq -r '.access_token'
|
|
574
|
+
# Should return a JWT string, not null
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Realm Exists
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
|
|
581
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
582
|
+
-d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
|
|
583
|
+
| jq -r '.access_token')
|
|
584
|
+
|
|
585
|
+
curl -s -X GET "http://localhost:8080/admin/realms/myapp" \
|
|
586
|
+
-H "Authorization: Bearer $TOKEN" | jq '.realm'
|
|
587
|
+
# Should return "myapp"
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Adapter Config Downloaded
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
cat ./data/tidecloak.json | jq '.realm'
|
|
594
|
+
# Should return your realm name
|
|
595
|
+
|
|
596
|
+
cat ./data/tidecloak.json | jq '.resource'
|
|
597
|
+
# Should return your client name
|
|
598
|
+
|
|
599
|
+
cat ./data/tidecloak.json | jq '.["auth-server-url"]'
|
|
600
|
+
# Should return your TideCloak URL
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### IGA Enabled
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
curl -s -X GET "http://localhost:8080/admin/realms/myapp" \
|
|
607
|
+
-H "Authorization: Bearer $TOKEN" | jq '.attributes.isIGAEnabled'
|
|
608
|
+
# Should return "true"
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Common Failures
|
|
614
|
+
|
|
615
|
+
### Wrong Mount Path
|
|
616
|
+
|
|
617
|
+
**Symptom**: `AccessDeniedException` or `Could not open file` errors in `docker logs tidecloak`. H2 database files appear in the project root.
|
|
618
|
+
|
|
619
|
+
**Cause**: Mounted the project root (`.`) instead of a dedicated `data/` subdirectory. The container writes DB files as UID 1000, conflicting with your project file ownership.
|
|
620
|
+
|
|
621
|
+
**Fix**:
|
|
622
|
+
```bash
|
|
623
|
+
docker stop tidecloak && docker rm tidecloak
|
|
624
|
+
rm -f keycloakdb* # Remove stale DB files from project root
|
|
625
|
+
mkdir -p ./data
|
|
626
|
+
sudo chown -R 1000:1000 ./data
|
|
627
|
+
# Restart with -v "$(pwd)/data:/opt/keycloak/data/h2"
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
### Permission Errors on Data Directory
|
|
633
|
+
|
|
634
|
+
**Symptom**: Container starts but crashes immediately. Logs show permission denied on `/opt/keycloak/data/h2`.
|
|
635
|
+
|
|
636
|
+
**Cause**: The `./data` directory is owned by root or your user, but the container process runs as UID 1000.
|
|
637
|
+
|
|
638
|
+
**Fix**:
|
|
639
|
+
```bash
|
|
640
|
+
sudo chown -R 1000:1000 ./data
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
### Port 8080 Already in Use
|
|
646
|
+
|
|
647
|
+
**Symptom**: `docker run` fails or container exits immediately. `docker logs tidecloak` shows bind error.
|
|
648
|
+
|
|
649
|
+
**Cause**: Another service (another TideCloak instance, a web server, Jenkins, etc.) is already using port 8080.
|
|
650
|
+
|
|
651
|
+
**Fix**:
|
|
652
|
+
```bash
|
|
653
|
+
# Find what is using the port
|
|
654
|
+
lsof -i :8080
|
|
655
|
+
|
|
656
|
+
# Either stop that service, or remap:
|
|
657
|
+
docker run -d --name tidecloak \
|
|
658
|
+
-v "$(pwd)/data:/opt/keycloak/data/h2" \
|
|
659
|
+
-p 9080:8080 \
|
|
660
|
+
...
|
|
661
|
+
# Then use TIDECLOAK_LOCAL_URL=http://localhost:9080
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
### Stale Volumes / Old Database State
|
|
667
|
+
|
|
668
|
+
**Symptom**: Realm creation fails with "realm already exists". Or Tide initialization returns unexpected errors. Or old users/clients appear from a previous run.
|
|
669
|
+
|
|
670
|
+
**Cause**: The `./data` directory contains H2 database files from a previous run.
|
|
671
|
+
|
|
672
|
+
**Fix**:
|
|
673
|
+
```bash
|
|
674
|
+
docker stop tidecloak && docker rm tidecloak
|
|
675
|
+
sudo rm -f ./data/keycloakdb*
|
|
676
|
+
sudo chown -R 1000:1000 ./data
|
|
677
|
+
# Restart the container
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
### Wrong Endpoint Paths
|
|
683
|
+
|
|
684
|
+
**Symptom**: `404 Not Found` from curl calls during initialization. Script silently fails at a step.
|
|
685
|
+
|
|
686
|
+
**Cause**: Incorrect URL construction. Common mistakes:
|
|
687
|
+
- Missing `/admin/` prefix on admin endpoints
|
|
688
|
+
- Using `/auth/` prefix (Keycloak < 17 pattern, not used by TideCloak)
|
|
689
|
+
- Wrong realm name in URL
|
|
690
|
+
|
|
691
|
+
**Fix**: Verify the endpoint paths from the Key Initialization Endpoints table above. TideCloak does not use the `/auth/` prefix. All admin endpoints start with `/admin/realms/{realm}/`.
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
### Missing Environment Variables (Staging Image)
|
|
696
|
+
|
|
697
|
+
**Symptom**: Container starts but Tide realm initialization (`setUpTideRealm`) fails or hangs. ORK-dependent operations time out.
|
|
698
|
+
|
|
699
|
+
**Cause**: Using `tidecloak-stg-dev` image without setting `SYSTEM_HOME_ORK`, `USER_HOME_ORK`, `THRESHOLD_T`, `THRESHOLD_N`, or `PAYER_PUBLIC`.
|
|
700
|
+
|
|
701
|
+
**Fix**: Provide all required staging environment variables. See the Environment Variables table. If you do not need Tide network connectivity, use `tidecloak-dev` instead.
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
### Admin Token Expired Mid-Script
|
|
706
|
+
|
|
707
|
+
**Symptom**: A curl call returns `401 Unauthorized` partway through initialization.
|
|
708
|
+
|
|
709
|
+
**Cause**: Admin tokens are short-lived. The script re-fetches tokens via `get_token()` before each group of calls, but long waits (e.g., waiting for admin to link their Tide account in step 8) can cause expiry.
|
|
710
|
+
|
|
711
|
+
**Fix**: The init script already calls `get_token()` before each major step. If you modify the script, always refresh the token before any API call that follows a wait or delay.
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
### Invite Link Expired or Missing
|
|
716
|
+
|
|
717
|
+
**Symptom**: The admin invite link returns an error page. Or `get-required-action-link` returns an empty response.
|
|
718
|
+
|
|
719
|
+
**Cause**: The `link-tide-account-action` required action is missing from `realm.json`, or the invite link lifespan (43200 seconds = 12 hours) has expired.
|
|
720
|
+
|
|
721
|
+
**Fix**: Verify `realm.json` includes the `requiredActions` section with `link-tide-account-action`. Regenerate the link:
|
|
722
|
+
```bash
|
|
723
|
+
TOKEN="$(get_token)"
|
|
724
|
+
curl -s -X POST \
|
|
725
|
+
"$TIDECLOAK_URL/admin/realms/$REALM_NAME/tideAdminResources/get-required-action-link?userId=$USER_ID&lifespan=43200" \
|
|
726
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
727
|
+
-H "Content-Type: application/json" \
|
|
728
|
+
-d '["link-tide-account-action"]'
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## Anti-Patterns
|
|
734
|
+
|
|
735
|
+
### Do Not Skip or Reorder Initialization Steps
|
|
736
|
+
|
|
737
|
+
The initialization sequence has strict ordering dependencies. Tide realm setup must happen before IGA enablement. Client change requests must be approved before admin user creation. User change requests must be approved after admin links their Tide account. Skipping or reordering steps causes silent failures or broken state.
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
### Do Not Minimize the realm.json Template
|
|
742
|
+
|
|
743
|
+
Every section in the template is required:
|
|
744
|
+
- `protocolMappers` -- without these, JWTs lack `tideuserkey`, `vuid`, and IGA role claims
|
|
745
|
+
- `authenticationFlows` + `authenticatorConfig` + `browserFlow` -- without these, login redirects to standard Keycloak login instead of Tide
|
|
746
|
+
- `requiredActions` -- without `link-tide-account-action`, admin invite links fail
|
|
747
|
+
- `roles` including `_tide_enabled` -- without this, Tide protocol features are disabled for users
|
|
748
|
+
- `components` (user profile) -- without this, user attributes behave incorrectly
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
752
|
+
### Do Not Use the Project Root as the Docker Volume Mount
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
# WRONG
|
|
756
|
+
docker run -v .:/opt/keycloak/data/h2 ...
|
|
757
|
+
|
|
758
|
+
# CORRECT
|
|
759
|
+
docker run -v ./data:/opt/keycloak/data/h2 ...
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
Mounting the project root causes H2 database files to be written alongside your source code with different ownership (UID 1000), leading to `AccessDeniedException` and polluted working directories.
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
### Do Not Hardcode Tokens
|
|
767
|
+
|
|
768
|
+
Admin tokens expire. Always call `get_token()` before each API call or group of calls. Do not capture a token once and reuse it across the entire script without refresh.
|
|
769
|
+
|
|
770
|
+
---
|
|
771
|
+
|
|
772
|
+
### Do Not Run the Init Script Against an Already-Initialized Realm
|
|
773
|
+
|
|
774
|
+
The script assumes a clean state. Running it twice creates duplicate users, fails on "realm already exists", or leaves change requests in an inconsistent state. If you need to re-initialize, clean up first:
|
|
775
|
+
```bash
|
|
776
|
+
docker stop tidecloak && docker rm tidecloak
|
|
777
|
+
sudo rm -f ./data/keycloakdb*
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
### Do Not Use tidecloak-dev Image When You Need IGA Approval Flows
|
|
783
|
+
|
|
784
|
+
The `tidecloak-dev` image is not connected to the Tide ORK network. Tide-specific features like `setUpTideRealm`, IGA change request signing, and admin invite links require the `tidecloak-stg-dev` image with ORK environment variables configured.
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
## References
|
|
789
|
+
|
|
790
|
+
- Source: operational exemplars: keylessh, forseti-crypto-quickstart, tidecloak-test-cases
|
|
791
|
+
- Reference scripts: keylessh `script/tidecloak/start.sh`, forseti-crypto-quickstart `init/tcinit.sh`, tidecloak-test-cases `tests/scripts/init-tidecloak.sh` + `handover-admin.sh`
|
|
792
|
+
- The tidecloak-test-cases `handover-admin.sh` provides a reusable CLI for admin setup: `-i` (invite link), `-c` (confirm linked), `-r` (assign role), `-a` (approve change-sets)
|