@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,654 @@
|
|
|
1
|
+
# Add Tide Auth to Fresh Next.js Project
|
|
2
|
+
|
|
3
|
+
Manual setup for integrating Tide authentication into a new Next.js application.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- Starting a new Next.js project (App Router or Pages Router)
|
|
10
|
+
- Need threshold password authentication (BYOiD)
|
|
11
|
+
- Need threshold JWT signing (VVK)
|
|
12
|
+
- Need optional E2EE or IGA features later
|
|
13
|
+
- Have a running TideCloak instance
|
|
14
|
+
|
|
15
|
+
**Do not use** if you already have an existing Next.js app with auth. See [add-auth-nextjs-existing.md](add-auth-nextjs-existing.md) instead.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
### Running Services
|
|
22
|
+
|
|
23
|
+
- TideCloak instance accessible (local or remote)
|
|
24
|
+
- TideCloak realm created and configured
|
|
25
|
+
- TideCloak client created in realm
|
|
26
|
+
- IGA enabled on realm (see [canon/concepts.md](../canon/concepts.md#iga-identity-governance--administration))
|
|
27
|
+
|
|
28
|
+
### Local Environment
|
|
29
|
+
|
|
30
|
+
- Node.js 18+
|
|
31
|
+
- Next.js 13+ installed. For automated/agent builds, prefer `npm init -y && npm install next react react-dom typescript @types/react @types/node` over `npx create-next-app@latest` — the latter has interactive prompts (React Compiler, etc.) that may block automation. VERIFIED (LEARNINGS-batch-009 L-04).
|
|
32
|
+
- TypeScript project recommended (all code examples use `.tsx`). If your project has no `tsconfig.json`, Next.js auto-generates one on first `npm run dev`.
|
|
33
|
+
- Package manager (npm, yarn, pnpm)
|
|
34
|
+
|
|
35
|
+
### Required Information
|
|
36
|
+
|
|
37
|
+
- TideCloak URL (e.g., `https://tidecloak.example.com`)
|
|
38
|
+
- Realm name
|
|
39
|
+
- Client ID
|
|
40
|
+
- Adapter JSON exported from TideCloak
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Files to Inspect First
|
|
45
|
+
|
|
46
|
+
Before starting, confirm these exist:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Next.js project structure
|
|
50
|
+
ls -la
|
|
51
|
+
# Should show: package.json, next.config.js, app/ or pages/
|
|
52
|
+
|
|
53
|
+
# Check Next.js version
|
|
54
|
+
cat package.json | grep '"next"'
|
|
55
|
+
# Should be 13.0.0 or higher
|
|
56
|
+
|
|
57
|
+
# Check router type
|
|
58
|
+
ls -la app/ 2>/dev/null && echo "App Router" || echo "Pages Router"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Files to Create/Edit
|
|
64
|
+
|
|
65
|
+
### Step Overview
|
|
66
|
+
|
|
67
|
+
1. Install SDK package
|
|
68
|
+
2. Create adapter JSON storage location
|
|
69
|
+
3. Add environment variables
|
|
70
|
+
4. Create provider component (App Router) or wrap `_app` (Pages Router)
|
|
71
|
+
5. Create silent SSO file
|
|
72
|
+
6. Create post-auth redirect handler
|
|
73
|
+
7. Configure CSP (observed requirement from keylessh)
|
|
74
|
+
8. Verify login flow
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Exact Step Sequence
|
|
79
|
+
|
|
80
|
+
### Step 1: Install Tide SDK
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install @tidecloak/nextjs
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Verify version**: All `@tidecloak/*` packages are currently at `0.13.x`. Run `npm view @tidecloak/nextjs version` to confirm. Do not assume `1.0.0`.
|
|
87
|
+
|
|
88
|
+
**Next.js 16+ bundler**: Next.js 16 defaults to Turbopack. The Tide SDK requires webpack for `strictExportPresence = false` and `@tidecloak/react` ESM alias. Update `package.json` scripts:
|
|
89
|
+
```json
|
|
90
|
+
"scripts": {
|
|
91
|
+
"dev": "next dev --webpack",
|
|
92
|
+
"build": "next build --webpack"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
Without `--webpack`, `next build` fails with: `"This build is using Turbopack, with a webpack config and no turbopack config"`. VERIFIED (LEARNINGS-session-003 L-02).
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### Step 2: Export Adapter JSON from TideCloak
|
|
100
|
+
|
|
101
|
+
**Via Admin Console**:
|
|
102
|
+
1. Login to TideCloak Admin Console
|
|
103
|
+
2. Navigate to: Clients → {your-client} → Installation
|
|
104
|
+
3. Select format: "Tide OIDC JSON" (NOT "Keycloak OIDC JSON")
|
|
105
|
+
4. Download file
|
|
106
|
+
|
|
107
|
+
**Via API** (Tide vendor endpoint — NOT the standard Keycloak installation path):
|
|
108
|
+
```bash
|
|
109
|
+
# First get the client UUID
|
|
110
|
+
CLIENT_UUID=$(curl -s "${TIDECLOAK_URL}/admin/realms/${REALM}/clients?clientId=${CLIENT_NAME}" \
|
|
111
|
+
-H "Authorization: Bearer ${ADMIN_TOKEN}" | jq -r '.[0].id')
|
|
112
|
+
|
|
113
|
+
# Export via vendorResources (returns jwk, vendorId, homeOrkUrl)
|
|
114
|
+
curl -s "${TIDECLOAK_URL}/admin/realms/${REALM}/vendorResources/get-installations-provider?clientId=${CLIENT_UUID}&providerId=keycloak-oidc-keycloak-json" \
|
|
115
|
+
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
|
116
|
+
> data/tidecloak.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Do NOT use** the standard Keycloak path (`/clients/{id}/installation/providers/keycloak-oidc-keycloak-json`) — it returns a minimal adapter missing `jwk`, `vendorId`, and `homeOrkUrl`. VERIFIED (LEARNINGS-batch-008 L-04).
|
|
120
|
+
|
|
121
|
+
**Store adapter JSON**:
|
|
122
|
+
```bash
|
|
123
|
+
# Create data directory
|
|
124
|
+
mkdir -p data
|
|
125
|
+
|
|
126
|
+
# Move downloaded adapter to data/tidecloak.json
|
|
127
|
+
mv ~/Downloads/tidecloak.json data/
|
|
128
|
+
|
|
129
|
+
# Verify Tide extensions present
|
|
130
|
+
node -e "const c = require('./data/tidecloak.json'); console.log('Has jwk:', !!c.jwk); console.log('Has vendorId:', !!c.vendorId); console.log('Has homeOrkUrl:', !!c.homeOrkUrl);"
|
|
131
|
+
# Should output: Has jwk: true, Has vendorId: true, Has homeOrkUrl: true
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Required**: The `jwk` field contains the embedded JWKS for local JWT verification. This field is present in Tide adapter exports due to Tide's non-rotating vendor-verifiable key (VVK) model. The system is locked to trust one pre-defined public key, ensuring that even if the IAM is compromised, it cannot forge valid tokens.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Step 3: Store Adapter JSON
|
|
139
|
+
|
|
140
|
+
The adapter JSON exported in Step 2 is the provider's config. Store it where the app can import it:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Add to .gitignore
|
|
144
|
+
echo "data/tidecloak.json" >> .gitignore # Contains sensitive config
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Note**: The `TideCloakProvider` takes the adapter JSON directly as a `config` prop. No environment variables are needed for the provider. Environment variables are only needed for the init script and server-side config loading.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### Step 4a: Provider Setup (App Router)
|
|
152
|
+
|
|
153
|
+
Create `app/providers.tsx`:
|
|
154
|
+
```typescript
|
|
155
|
+
// app/providers.tsx
|
|
156
|
+
'use client';
|
|
157
|
+
|
|
158
|
+
import { TideCloakProvider } from '@tidecloak/nextjs';
|
|
159
|
+
import tcConfig from '../../data/tidecloak.json';
|
|
160
|
+
|
|
161
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
162
|
+
return (
|
|
163
|
+
<TideCloakProvider
|
|
164
|
+
config={{ ...tcConfig, useDPoP: { mode: 'strict', alg: 'ES256' } }}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</TideCloakProvider>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**`useDPoP` goes inside the config object**, not as a separate JSX prop. The `TideCloakProvider` only accepts `config` and `children`. VERIFIED (session-002).
|
|
173
|
+
|
|
174
|
+
**DPoP is a bidirectional lockstep requirement** (I-12):
|
|
175
|
+
1. **Server-side**: Realm template sets client attribute `"dpop.bound.access.tokens": "true"`.
|
|
176
|
+
2. **Client-side**: `useDPoP` in the config object tells the SDK to generate DPoP proofs.
|
|
177
|
+
3. **Both must be set simultaneously.** Server-side without client-side → token endpoint returns 400 "DPoP proof is missing." Client-side without server-side → proofs generated but ignored.
|
|
178
|
+
|
|
179
|
+
**`tide_dpop_auth.html` required** (when DPoP is enabled):
|
|
180
|
+
- Copy `tide_dpop_auth.html` to `public/`. This file is loaded by the Tide enclave during login to prove DPoP key possession to the ORKs. **Do not modify it** — its content is integrity-checked. VERIFIED (learning-batch-004, L-07).
|
|
181
|
+
- **The HTML must match the SDK version.** The only source for this file is the pack template (`templates/*/public/tide_dpop_auth.html`). It is NOT shipped inside `@tidecloak/*` npm packages. If the pack template is stale and the enclave rejects it with `Popup DPoP verification failed to load`, contact the Tide team for the updated file. VERIFIED (LEARNINGS-batch-005 L-05, LEARNINGS-batch-007 L-03).
|
|
182
|
+
- The SDK requests it at `/tide_dpop/iss/<hex-issuer>/aud/<hex-client>/tide_dpop_auth.html` — a path that doesn't map to the static file in `public/`. Use `next.config.ts` `rewrites()` to map `/tide_dpop/:path*` → `/tide_dpop_auth.html`.
|
|
183
|
+
- **Do NOT use a route handler** (`app/tide_dpop/[...path]/route.ts`). Next.js 16 dev server injects its own hash-based CSP on route handler responses, overriding `script-src 'unsafe-inline'`. The DPoP page's inline script gets blocked. Static files served via rewrites are not affected. VERIFIED (LEARNINGS-batch-005 L-04).
|
|
184
|
+
- **Do NOT validate issuer/client hex params** in the handler. The enclave already integrity-checks the HTML. Server-side validation that fails (config loading, path resolution) returns 400 before the HTML loads, killing the popup. VERIFIED (LEARNINGS-batch-005 L-03).
|
|
185
|
+
- Set CSP via `next.config.ts` `headers()` targeting `/tide_dpop/:path*`: `Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline'` and `Allow-CSP-From: *`.
|
|
186
|
+
- Without this file and rewrite, DPoP login fails with: `Tide user did not provided a dpop bound token but a dpop cnf claim was found in requested token`.
|
|
187
|
+
|
|
188
|
+
**`secureFetch` usage rules** (when `useDPoP` is enabled):
|
|
189
|
+
- Use `IAMService.secureFetch` from `@tidecloak/js` with `await IAMService.getToken()` for the managed token. **`getToken()` returns a Promise** — you must `await` it. Without `await`, the Authorization header becomes `Bearer [object Promise]` and the server rejects with `JWSInvalid: Invalid Compact JWS`. VERIFIED (LEARNINGS-batch-005 L-07).
|
|
190
|
+
- `secureFetch` requires **absolute URLs** — relative paths throw. Use `\`\${window.location.origin}/api/vault\``.
|
|
191
|
+
- **Recommended**: Create an `appFetch` wrapper. See canon/anti-patterns.md for the pattern.
|
|
192
|
+
|
|
193
|
+
Edit `app/layout.tsx`:
|
|
194
|
+
```typescript
|
|
195
|
+
// app/layout.tsx
|
|
196
|
+
import { Providers } from './providers';
|
|
197
|
+
|
|
198
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
199
|
+
return (
|
|
200
|
+
<html lang="en">
|
|
201
|
+
<body>
|
|
202
|
+
<Providers>{children}</Providers>
|
|
203
|
+
</body>
|
|
204
|
+
</html>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Step 4b: Provider Setup (Pages Router)
|
|
212
|
+
|
|
213
|
+
Edit `pages/_app.tsx`:
|
|
214
|
+
```typescript
|
|
215
|
+
// pages/_app.tsx
|
|
216
|
+
import type { AppProps } from 'next/app';
|
|
217
|
+
import { TideCloakProvider } from '@tidecloak/nextjs';
|
|
218
|
+
import tcConfig from '../data/tidecloak.json';
|
|
219
|
+
|
|
220
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
221
|
+
return (
|
|
222
|
+
<TideCloakProvider
|
|
223
|
+
config={{ ...tcConfig, useDPoP: { mode: 'strict', alg: 'ES256' } }}
|
|
224
|
+
>
|
|
225
|
+
<Component {...pageProps} />
|
|
226
|
+
</TideCloakProvider>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Step 5: Create Silent SSO File
|
|
234
|
+
|
|
235
|
+
**Required for silent token refresh** (VERIFIED):
|
|
236
|
+
```bash
|
|
237
|
+
# Create public/silent-check-sso.html
|
|
238
|
+
mkdir -p public
|
|
239
|
+
cat > public/silent-check-sso.html << 'EOF'
|
|
240
|
+
<html>
|
|
241
|
+
<body>
|
|
242
|
+
<script>
|
|
243
|
+
parent.postMessage(location.href, location.origin);
|
|
244
|
+
</script>
|
|
245
|
+
</body>
|
|
246
|
+
</html>
|
|
247
|
+
EOF
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Why required**: OIDC silent refresh uses hidden iframe. TideCloak redirects to this file during refresh. Without it, silent refresh fails and users are forced to re-login. See [canon/invariants.md I-07](../canon/invariants.md#i-07-silent-sso-file-required).
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### Step 6: Create Post-Auth Redirect Handler
|
|
255
|
+
|
|
256
|
+
**Required for login completion** (I-16). After authentication at TideCloak, the browser redirects back to the app at the configured `redirectUri`. A real page must exist at that path. See [canon/redirect-handler.md](../canon/redirect-handler.md).
|
|
257
|
+
|
|
258
|
+
**App Router** — create `app/auth/redirect/page.tsx`:
|
|
259
|
+
```typescript
|
|
260
|
+
// app/auth/redirect/page.tsx
|
|
261
|
+
"use client";
|
|
262
|
+
|
|
263
|
+
import { useAuthCallback } from "@tidecloak/nextjs";
|
|
264
|
+
import { useEffect, useState } from "react";
|
|
265
|
+
|
|
266
|
+
// Separated so useAuthCallback only runs after hydration (avoids SSR window error).
|
|
267
|
+
function RedirectHandler() {
|
|
268
|
+
const { isProcessing, isSuccess, error } = useAuthCallback({
|
|
269
|
+
onSuccess: (returnUrl) => {
|
|
270
|
+
window.location.assign(returnUrl || "/");
|
|
271
|
+
},
|
|
272
|
+
onError: () => {
|
|
273
|
+
window.location.assign("/");
|
|
274
|
+
},
|
|
275
|
+
onMissingVerifierRedirectTo: "/",
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
const params = new URLSearchParams(window.location.search);
|
|
280
|
+
if (!params.has("code") && !params.has("error")) {
|
|
281
|
+
window.location.assign("/");
|
|
282
|
+
}
|
|
283
|
+
}, []);
|
|
284
|
+
|
|
285
|
+
if (error) return <p>Authentication failed: {error.message}</p>;
|
|
286
|
+
if (isProcessing || !isSuccess) return <p>Completing login...</p>;
|
|
287
|
+
return <p>Redirecting...</p>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export default function AuthRedirectPage() {
|
|
291
|
+
const [mounted, setMounted] = useState(false);
|
|
292
|
+
useEffect(() => setMounted(true), []);
|
|
293
|
+
if (!mounted) return <p>Loading...</p>;
|
|
294
|
+
return <RedirectHandler />;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The `useAuthCallback` hook processes the OIDC callback (code exchange) and calls `onSuccess` with the `returnUrl`. On error or missing PKCE verifier (page refresh during login), it redirects back to home.
|
|
299
|
+
|
|
300
|
+
**Do not** use a bare placeholder page with no callback handling. The page must actively process the callback and redirect.
|
|
301
|
+
|
|
302
|
+
**Pages Router** — create `pages/auth/redirect.tsx` (same pattern, adapted for Pages Router).
|
|
303
|
+
|
|
304
|
+
**Also update TideCloak client settings**:
|
|
305
|
+
- TideCloak Admin → Clients → {client} → Settings
|
|
306
|
+
- Valid Redirect URIs: add `http://localhost:3000/auth/redirect`
|
|
307
|
+
|
|
308
|
+
**Failure if missing**: Login completes at TideCloak but the user lands on a 404. The SDK never processes the auth code. See [canon/redirect-handler.md](../canon/redirect-handler.md) for diagnostics.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Step 7: Configure CSP for SWE Iframe
|
|
313
|
+
|
|
314
|
+
**Required**: The SWE (Secure Web Enclave) iframe must be allowed to load from any Tide ORK host because users can re-home their sessions to their preferred ORK. By Tide's security design, while a platform vendor may designate a home ORK, users can override this choice and switch to any ORK they trust.
|
|
315
|
+
|
|
316
|
+
**For development**, use a permissive CSP:
|
|
317
|
+
|
|
318
|
+
Edit `next.config.js`:
|
|
319
|
+
```javascript
|
|
320
|
+
// next.config.js
|
|
321
|
+
/** @type {import('next').NextConfig} */
|
|
322
|
+
const nextConfig = {
|
|
323
|
+
async headers() {
|
|
324
|
+
return [
|
|
325
|
+
{
|
|
326
|
+
source: '/:path*',
|
|
327
|
+
headers: [
|
|
328
|
+
{
|
|
329
|
+
key: 'Content-Security-Policy',
|
|
330
|
+
value: "frame-src 'self' *"
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
module.exports = nextConfig;
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**For production**, you may restrict to known Tide domains, but understand this limits user choice of ORK:
|
|
342
|
+
```javascript
|
|
343
|
+
value: "frame-src 'self' https://*.tideprotocol.com https://*.dauth.me"
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Failure symptom if missing**: Login hangs indefinitely. Browser console shows CSP violation: `Refused to frame 'https://...' because it violates the following Content Security Policy directive`. See [canon/troubleshooting.md T-01](../canon/troubleshooting.md#t-01-login-hangs-indefinitely).
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### Step 8: Create Login Component
|
|
351
|
+
|
|
352
|
+
Create basic login UI:
|
|
353
|
+
|
|
354
|
+
**App Router** (`app/page.tsx`):
|
|
355
|
+
```typescript
|
|
356
|
+
// app/page.tsx
|
|
357
|
+
'use client';
|
|
358
|
+
|
|
359
|
+
import { useTideCloak } from '@tidecloak/nextjs';
|
|
360
|
+
|
|
361
|
+
export default function HomePage() {
|
|
362
|
+
const { authenticated, getValueFromIdToken, login, logout } = useTideCloak();
|
|
363
|
+
|
|
364
|
+
if (!authenticated) {
|
|
365
|
+
return (
|
|
366
|
+
<div>
|
|
367
|
+
<h1>Welcome</h1>
|
|
368
|
+
<button onClick={login}>Login with Tide</button>
|
|
369
|
+
</div>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
<div>
|
|
375
|
+
<h1>Welcome, {getValueFromIdToken("preferred_username")}</h1>
|
|
376
|
+
<button onClick={logout}>Logout</button>
|
|
377
|
+
</div>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Pages Router** (`pages/index.tsx`):
|
|
383
|
+
```typescript
|
|
384
|
+
// pages/index.tsx
|
|
385
|
+
import { useTideCloak } from '@tidecloak/nextjs';
|
|
386
|
+
|
|
387
|
+
export default function HomePage() {
|
|
388
|
+
const { authenticated, getValueFromIdToken, login, logout } = useTideCloak();
|
|
389
|
+
|
|
390
|
+
if (!authenticated) {
|
|
391
|
+
return (
|
|
392
|
+
<div>
|
|
393
|
+
<h1>Welcome</h1>
|
|
394
|
+
<button onClick={login}>Login with Tide</button>
|
|
395
|
+
</div>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return (
|
|
400
|
+
<div>
|
|
401
|
+
<h1>Welcome, {getValueFromIdToken("preferred_username")}</h1>
|
|
402
|
+
<button onClick={logout}>Logout</button>
|
|
403
|
+
</div>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
### Step 9: Test Login Flow
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# Start dev server
|
|
414
|
+
npm run dev
|
|
415
|
+
|
|
416
|
+
# Open browser to http://localhost:3000
|
|
417
|
+
# Click "Login with Tide"
|
|
418
|
+
# Should redirect to TideCloak login page
|
|
419
|
+
# Enter credentials
|
|
420
|
+
# Should redirect back to app, showing "Welcome, {username}"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Verification Checklist
|
|
426
|
+
|
|
427
|
+
After completing all steps, verify:
|
|
428
|
+
|
|
429
|
+
### Client-Side
|
|
430
|
+
|
|
431
|
+
- [ ] Login button visible when unauthenticated
|
|
432
|
+
- [ ] Click login redirects to TideCloak
|
|
433
|
+
- [ ] Enter credentials and submit
|
|
434
|
+
- [ ] Redirect back to app within 5-10 seconds
|
|
435
|
+
- [ ] User name displays correctly
|
|
436
|
+
- [ ] Logout button visible when authenticated
|
|
437
|
+
- [ ] Click logout clears session and shows login button again
|
|
438
|
+
|
|
439
|
+
### Browser Console
|
|
440
|
+
|
|
441
|
+
- [ ] No CSP violations (`frame-src` errors)
|
|
442
|
+
- [ ] No 404 on `silent-check-sso.html`
|
|
443
|
+
- [ ] No 404 on redirect handler path (`/auth/redirect`)
|
|
444
|
+
- [ ] No CORS errors
|
|
445
|
+
- [ ] No "Failed to load resource" for Tide domains
|
|
446
|
+
|
|
447
|
+
### Browser DevTools → Network
|
|
448
|
+
|
|
449
|
+
- [ ] Redirect to TideCloak `/auth` endpoint
|
|
450
|
+
- [ ] Redirect back to app with `code` parameter
|
|
451
|
+
- [ ] Token exchange request to TideCloak `/token` endpoint
|
|
452
|
+
- [ ] Requests to `*.tideprotocol.com` or `*.dauth.me` (SWE iframe)
|
|
453
|
+
|
|
454
|
+
### Browser DevTools → Application → Local Storage
|
|
455
|
+
|
|
456
|
+
- [ ] Keys starting with `kc-` or `tc-` present after login
|
|
457
|
+
- [ ] JWT token stored (decode at jwt.io to verify claims)
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Common Failures
|
|
462
|
+
|
|
463
|
+
### Login Hangs Indefinitely
|
|
464
|
+
|
|
465
|
+
**Symptom**: Redirect to TideCloak succeeds, but redirect back never completes. Spinner forever.
|
|
466
|
+
|
|
467
|
+
**Cause**: CSP blocks SWE iframe (most common).
|
|
468
|
+
|
|
469
|
+
**Fix**: Check browser console for CSP violation. Add Tide domains to CSP in `next.config.js` (Step 6).
|
|
470
|
+
|
|
471
|
+
**See**: [canon/troubleshooting.md T-01](../canon/troubleshooting.md#t-01-login-hangs-indefinitely)
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
### 404 on silent-check-sso.html
|
|
476
|
+
|
|
477
|
+
**Symptom**: Silent refresh fails after 10 minutes. User forced to re-login.
|
|
478
|
+
|
|
479
|
+
**Cause**: Missing `public/silent-check-sso.html`.
|
|
480
|
+
|
|
481
|
+
**Fix**: Create file (Step 5). Verify accessible at `http://localhost:3000/silent-check-sso.html`.
|
|
482
|
+
|
|
483
|
+
**See**: [canon/troubleshooting.md T-04](../canon/troubleshooting.md#t-04-silent-token-refresh-fails)
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
### "Cannot find module @tidecloak/nextjs"
|
|
488
|
+
|
|
489
|
+
**Symptom**: Import error on `TideCloakContextProvider`.
|
|
490
|
+
|
|
491
|
+
**Cause**: Package not installed or not on public npm registry.
|
|
492
|
+
|
|
493
|
+
**Fix**: Verify `npm install @tidecloak/nextjs` succeeds. Check `package.json` includes `@tidecloak/nextjs` in dependencies. Contact Tide team if package unavailable.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
### Config Not Loading
|
|
498
|
+
|
|
499
|
+
**Symptom**: Provider fails to initialize, auth-server-url undefined.
|
|
500
|
+
|
|
501
|
+
**Cause**: `data/tidecloak.json` missing or not imported by provider.
|
|
502
|
+
|
|
503
|
+
**Fix**:
|
|
504
|
+
- Verify `data/tidecloak.json` exists: `test -f data/tidecloak.json`
|
|
505
|
+
- Verify `providers.tsx` imports it: `import tcConfig from '../../data/tidecloak.json'`
|
|
506
|
+
- Verify provider uses `config={tcConfig}`, not `NEXT_PUBLIC_TIDECLOAK_*` env vars
|
|
507
|
+
- Restart dev server (`npm run dev`)
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
### Redirect URI Mismatch
|
|
512
|
+
|
|
513
|
+
**Symptom**: TideCloak error: "Invalid redirect_uri".
|
|
514
|
+
|
|
515
|
+
**Cause**: Client in TideCloak not configured with app's redirect URI.
|
|
516
|
+
|
|
517
|
+
**Fix**:
|
|
518
|
+
1. TideCloak Admin → Clients → {your-client} → Settings
|
|
519
|
+
2. Add to "Valid Redirect URIs": `http://localhost:3000/*`
|
|
520
|
+
3. Add to "Valid Post Logout Redirect URIs": `http://localhost:3000/*`
|
|
521
|
+
4. Save
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Repair Path
|
|
526
|
+
|
|
527
|
+
If setup fails completely:
|
|
528
|
+
|
|
529
|
+
1. **Clear state**:
|
|
530
|
+
```bash
|
|
531
|
+
# Browser: DevTools → Application → Local Storage → Clear All
|
|
532
|
+
# Or visit: http://localhost:3000?reset=true (if implemented)
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
2. **Verify TideCloak is reachable**:
|
|
536
|
+
```bash
|
|
537
|
+
curl ${TIDECLOAK_URL}/realms/${REALM}/.well-known/openid-configuration
|
|
538
|
+
# Should return JSON with auth endpoints
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
3. **Verify adapter JSON is valid**:
|
|
542
|
+
```bash
|
|
543
|
+
node -e "console.log(require('./data/tidecloak.json'))"
|
|
544
|
+
# Should output valid JSON
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
4. **Check Next.js logs**:
|
|
548
|
+
```bash
|
|
549
|
+
npm run dev
|
|
550
|
+
# Watch for errors during startup
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
5. **Test in incognito**:
|
|
554
|
+
- Clear browser state may not be complete
|
|
555
|
+
- Incognito ensures fresh session
|
|
556
|
+
|
|
557
|
+
6. **Consult troubleshooting**:
|
|
558
|
+
- [canon/troubleshooting.md](../canon/troubleshooting.md)
|
|
559
|
+
- [canon/anti-patterns.md](../canon/anti-patterns.md)
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Do Not Do This
|
|
564
|
+
|
|
565
|
+
### ❌ Scaffold Command (Alternative)
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# Alternative: Automated scaffold
|
|
569
|
+
npm init @tidecloak/nextjs@latest <myApp>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**About**: The scaffold command automates realm creation, IGA setup, client configuration, and adapter export. It prompts for TideCloak URL, realm name, client name, admin credentials, and operator email for license activation. The process creates a complete Next.js app with Tide auth pre-configured.
|
|
573
|
+
|
|
574
|
+
**Why manual setup is preferred here**: Manual setup is more inspectable and educational. Scaffold is useful for quick starts but obscures the individual configuration steps. Choose based on your learning goals.
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
### ❌ Do Not Skip CSP Configuration
|
|
579
|
+
|
|
580
|
+
```javascript
|
|
581
|
+
// ❌ WRONG: No CSP or missing Tide domains
|
|
582
|
+
// SWE iframe will be blocked
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Why**: Browser blocks cross-origin iframes by default. SWE cannot load without CSP whitelist. Login hangs forever with no visible error. See [canon/anti-patterns.md AP-07](../canon/anti-patterns.md#ap-07-missing-csp-whitelist).
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
### ❌ Do Not Skip silent-check-sso.html
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
# ❌ WRONG: Missing silent SSO file
|
|
593
|
+
# Silent refresh will fail
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Why**: Silent token refresh requires this file. Without it, users forced to re-login every 10 minutes (or token expiration). See [canon/anti-patterns.md AP-15](../canon/anti-patterns.md#ap-15-skipping-silent-check-ssohtml).
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
### ❌ Do Not Store Secrets in Client-Side Env Vars
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
# ❌ WRONG: Client secret in NEXT_PUBLIC_ var
|
|
604
|
+
NEXT_PUBLIC_CLIENT_SECRET=secret123
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**Why**: `NEXT_PUBLIC_` vars are bundled into client JavaScript. Visible to all users. Tide uses public clients (no client secret). If you have a confidential client, use server-side env vars only.
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
### ❌ Do Not Implement Custom Login Flow
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
// ❌ WRONG: Custom password submission
|
|
615
|
+
async function handleLogin(username: string, password: string) {
|
|
616
|
+
const response = await fetch(`${tidecloakUrl}/token`, {
|
|
617
|
+
method: 'POST',
|
|
618
|
+
body: new URLSearchParams({ username, password, grant_type: 'password' })
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Why**: Tide authentication is threshold-verified (PRISM). Custom password flows bypass threshold enforcement. Use SDK's `login()` method. See [canon/anti-patterns.md AP-14](../canon/anti-patterns.md#ap-14-implementing-custom-token-refresh-logic).
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## Known Uncertainties
|
|
628
|
+
|
|
629
|
+
### Required `_tide_*` Role
|
|
630
|
+
|
|
631
|
+
Each user must have at least one role starting with `_tide_` (e.g., `_tide_enabled`) for Tide operations to work. This is required for the vendor to fund Tide operations through the voucher system. Users without a `_tide_*` role will have their Tide requests rejected because no voucher will be assigned.
|
|
632
|
+
|
|
633
|
+
**Setup**: Ensure your realm's default roles include at least one `_tide_*` role, or assign such roles manually to users.
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Next Steps
|
|
638
|
+
|
|
639
|
+
After login works, follow this sequence:
|
|
640
|
+
|
|
641
|
+
1. [Protect routes](protect-routes-nextjs.md) - Client-side route guards (UI gating only)
|
|
642
|
+
2. [Protect APIs](protect-api-nextjs.md) - Server-side JWT verification (real authorization). **Required before RBAC.**
|
|
643
|
+
3. [Add RBAC](add-rbac-nextjs.md) - Role-based access control (depends on API protection)
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## References
|
|
648
|
+
|
|
649
|
+
- [canon/concepts.md](../canon/concepts.md) - Tide core concepts
|
|
650
|
+
- [canon/framework-matrix.md](../canon/framework-matrix.md) - Next.js implementation patterns
|
|
651
|
+
- [canon/invariants.md](../canon/invariants.md) - Security rules (I-06, I-07)
|
|
652
|
+
- [canon/anti-patterns.md](../canon/anti-patterns.md) - Common mistakes (AP-07, AP-14, AP-15)
|
|
653
|
+
- [canon/troubleshooting.md](../canon/troubleshooting.md) - Symptom-led debugging (T-01, T-04)
|
|
654
|
+
- keylessh exemplar: `sources/example-app-keylessh/` (DC-04, DC-09)
|