@tidecloak/js 0.13.11 → 0.13.14
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/README.md +18 -503
- package/dist/cjs/src/AdminAPI.js +419 -0
- package/dist/cjs/src/AdminAPI.js.map +1 -0
- package/dist/cjs/src/IAMService.js +924 -9
- package/dist/cjs/src/IAMService.js.map +1 -1
- package/dist/cjs/src/index.js +8 -0
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/types.js +5 -0
- package/dist/cjs/src/types.js.map +1 -0
- package/dist/esm/src/AdminAPI.js +419 -0
- package/dist/esm/src/AdminAPI.js.map +1 -0
- package/dist/esm/src/IAMService.js +924 -9
- package/dist/esm/src/IAMService.js.map +1 -1
- package/dist/esm/src/index.js +8 -0
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/types.js +5 -0
- package/dist/esm/src/types.js.map +1 -0
- package/dist/types/AdminAPI.d.ts +102 -0
- package/dist/types/IAMService.d.ts +160 -6
- package/dist/types/index.d.ts +6 -0
- package/dist/types/src/AdminAPI.d.ts +102 -0
- package/dist/types/src/IAMService.d.ts +160 -6
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/types.d.ts +112 -0
- package/dist/types/types.d.ts +112 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,520 +1,35 @@
|
|
|
1
|
-
# TideCloak JavaScript SDK
|
|
1
|
+
# TideCloak JavaScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Authentication Modes
|
|
6
|
-
|
|
7
|
-
The SDK supports two authentication modes:
|
|
8
|
-
|
|
9
|
-
| Mode | Description | Use Case |
|
|
10
|
-
|------|-------------|----------|
|
|
11
|
-
| **Front-channel** | Browser handles all token operations (standard OIDC) | SPAs, simple apps |
|
|
12
|
-
| **Hybrid/BFF** | Browser handles PKCE, backend exchanges code for tokens | Secure apps, server-side sessions |
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 1. Prerequisites
|
|
17
|
-
|
|
18
|
-
Before you begin, ensure you have:
|
|
19
|
-
|
|
20
|
-
* A bundler like [Vite](https://vite.dev/) or [Webpack](https://webpack.js.org/)
|
|
21
|
-
* A [running](https://github.com/tide-foundation/tidecloak-gettingstarted) TideCloak server
|
|
22
|
-
* A registered client in your realm with default user contexts committed
|
|
23
|
-
* A valid Keycloak adapter JSON file (e.g., `tidecloak.json`)
|
|
24
|
-
* A browser environment (SDK uses `window`, `document.cookie`, etc.)
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## 2. Project Setup
|
|
29
|
-
|
|
30
|
-
Start a new project with Vite + TideCloak:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm create vite@latest my-app -- --template vanilla
|
|
34
|
-
cd my-app
|
|
35
|
-
npm install
|
|
36
|
-
npm run dev
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Folder structure:
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
my-app/
|
|
43
|
-
├─ index.html
|
|
44
|
-
├─ main.js
|
|
45
|
-
├─ tidecloak.json
|
|
46
|
-
├─ public/
|
|
47
|
-
│ └─ auth/
|
|
48
|
-
│ └─ redirect.html
|
|
49
|
-
├─ package.json
|
|
50
|
-
└─ vite.config.js
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## 3. Install `@tidecloak/js`
|
|
3
|
+
Add TideCloak authentication to any JavaScript app.
|
|
56
4
|
|
|
57
5
|
```bash
|
|
58
6
|
npm install @tidecloak/js
|
|
59
|
-
# or
|
|
60
|
-
yarn add @tidecloak/js
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
This package exports:
|
|
64
|
-
|
|
65
|
-
* `IAMService` - high-level wrapper and lifecycle manager
|
|
66
|
-
* `TideCloak` - lower-level Keycloak-style adapter instance
|
|
67
|
-
|
|
68
|
-
> **Note:** Installing this package automatically adds a `silent-check-sso.html` file to your `public` directory. This file is required for silent SSO checks; if it doesn’t exist, create it manually at `public/silent-check-sso.html` with the following content, otherwise the app will break:
|
|
69
|
-
>
|
|
70
|
-
> ```html
|
|
71
|
-
> <html>
|
|
72
|
-
> <body>
|
|
73
|
-
> <script>parent.postMessage(location.href, location.origin)</script>
|
|
74
|
-
> </body>
|
|
75
|
-
> </html>
|
|
76
|
-
> ```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## 4. Initialize the SDK
|
|
81
|
-
|
|
82
|
-
In your main entry file, initialize IAM and register lifecycle listeners. You may also choose to handle lifecycle events such as session expiration here:
|
|
83
|
-
|
|
84
|
-
**File:** `main.js`
|
|
85
|
-
|
|
86
|
-
```js
|
|
87
|
-
import { IAMService } from "@tidecloak/js";
|
|
88
|
-
import config from "./tidecloak.json";
|
|
89
|
-
|
|
90
|
-
const loginBtn = document.getElementById("login-btn");
|
|
91
|
-
const logoutBtn = document.getElementById("logout-btn");
|
|
92
|
-
const statusEl = document.getElementById("status");
|
|
93
|
-
|
|
94
|
-
loginBtn.onclick = () => IAMService.doLogin();
|
|
95
|
-
logoutBtn.onclick = () => IAMService.doLogout();
|
|
96
|
-
|
|
97
|
-
function updateUI(authenticated) {
|
|
98
|
-
loginBtn.style.display = authenticated ? "none" : "inline-block";
|
|
99
|
-
logoutBtn.style.display = authenticated ? "inline-block" : "none";
|
|
100
|
-
statusEl.textContent = authenticated ? "✅ Authenticated" : "🔒 Please log in";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
IAMService
|
|
104
|
-
.on("ready", updateUI)
|
|
105
|
-
.on("authError", err => statusEl.textContent = `❌ Auth error: ${err.message}`)
|
|
106
|
-
.on("logout", () => {
|
|
107
|
-
console.log("User logged out");
|
|
108
|
-
updateUI(false);
|
|
109
|
-
})
|
|
110
|
-
.on("tokenExpired", () => {
|
|
111
|
-
alert("Session expired, please log in again");
|
|
112
|
-
updateUI(false);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
(async () => {
|
|
116
|
-
try {
|
|
117
|
-
await IAMService.initIAM(config); // You can add redirectUri here if customizing
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.error("Failed to initialize IAM:", err);
|
|
120
|
-
statusEl.textContent = "❌ Initialization error";
|
|
121
|
-
}
|
|
122
|
-
})();
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**File:** `index.html`
|
|
126
|
-
|
|
127
|
-
```html
|
|
128
|
-
<button id="login-btn">Log In</button>
|
|
129
|
-
<button id="logout-btn" style="display:none">Log Out</button>
|
|
130
|
-
<div id="status">Initializing...</div>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## 5. Redirect URI Handling
|
|
136
|
-
|
|
137
|
-
TideCloak will redirect users after login/logout to a URI defined in your adapter config.
|
|
138
|
-
|
|
139
|
-
If not explicitly set, the default value is:
|
|
140
|
-
|
|
141
|
-
```js
|
|
142
|
-
`${window.location.origin}/auth/redirect`
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
> This means your app **must contain a static file or route** at `/auth/redirect`.
|
|
146
|
-
> In Vite, this typically means adding a file like `public/auth/redirect.html`.
|
|
147
|
-
|
|
148
|
-
You can override this behavior by passing a `redirectUri` to `initIAM()`:
|
|
149
|
-
|
|
150
|
-
```js
|
|
151
|
-
await IAMService.initIAM({
|
|
152
|
-
...config,
|
|
153
|
-
redirectUri: "https://yourdomain.com/auth/callback"
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
> ⚠️ Regardless of the value used, the **actual route or file must exist** in your deployed project. If the redirect target doesn’t exist, users will land on a 404 page after login/logout.
|
|
158
|
-
|
|
159
|
-
**File:** `public/auth/redirect.html`
|
|
160
|
-
|
|
161
|
-
```html
|
|
162
|
-
<!-- This file ensures the /auth/redirect path exists -->
|
|
163
|
-
<!DOCTYPE html>
|
|
164
|
-
<html>
|
|
165
|
-
<head><title>Redirecting...</title></head>
|
|
166
|
-
<body>
|
|
167
|
-
<p>Redirecting, please wait...</p>
|
|
168
|
-
<script>
|
|
169
|
-
// Optionally show loading UI or transition
|
|
170
|
-
// Auth state will be handled once initIAM runs again in your main.js
|
|
171
|
-
window.location.href = "/"; // or redirect elsewhere
|
|
172
|
-
</script>
|
|
173
|
-
</body>
|
|
174
|
-
</html>
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
**Description:** This file ensures that the default redirect URI resolves without a 404.
|
|
178
|
-
|
|
179
|
-
If you override the `redirectUri` in `initIAM`, make sure to **update the corresponding redirect path** and that it exists in `public/` or your router.
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## 6. Hybrid/BFF Mode (Backend-For-Frontend)
|
|
184
|
-
|
|
185
|
-
In hybrid mode, the browser generates PKCE and redirects to the IdP, but the **backend exchanges the authorization code for tokens**. This keeps tokens server-side for improved security—ideal for applications with server-rendered pages or strict security requirements.
|
|
186
|
-
|
|
187
|
-
### How It Works
|
|
188
|
-
|
|
189
|
-
1. **Login Page**: User clicks login → `doLogin()` generates PKCE, stores verifier in sessionStorage, redirects to IdP
|
|
190
|
-
2. **IdP**: User authenticates
|
|
191
|
-
3. **Callback Page**: IdP redirects back with `?code=...` → `initIAM()` sends code + verifier to your backend
|
|
192
|
-
4. **Backend**: Exchanges code for tokens, creates session cookie
|
|
193
|
-
5. **Success**: User is redirected to their original destination
|
|
194
|
-
|
|
195
|
-
### Configuration
|
|
196
|
-
|
|
197
|
-
```js
|
|
198
|
-
const hybridConfig = {
|
|
199
|
-
authMode: "hybrid",
|
|
200
|
-
oidc: {
|
|
201
|
-
authorizationEndpoint: "https://auth.example.com/realms/myrealm/protocol/openid-connect/auth",
|
|
202
|
-
clientId: "my-client",
|
|
203
|
-
redirectUri: "https://app.example.com/auth/callback",
|
|
204
|
-
scope: "openid profile email", // optional, defaults to "openid profile email"
|
|
205
|
-
prompt: "login" // optional
|
|
206
|
-
},
|
|
207
|
-
tokenExchange: {
|
|
208
|
-
endpoint: "/api/authenticate", // Your backend endpoint
|
|
209
|
-
provider: "tidecloak-auth", // optional, sent to backend
|
|
210
|
-
headers: () => ({ // optional, custom headers (e.g., CSRF token)
|
|
211
|
-
"anti-csrf-token": document.querySelector('meta[name="csrf-token"]')?.content
|
|
212
|
-
})
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Login Page Example
|
|
218
|
-
|
|
219
|
-
```js
|
|
220
|
-
import { IAMService } from "@tidecloak/js";
|
|
221
|
-
|
|
222
|
-
// Define config (or import from shared file)
|
|
223
|
-
const hybridConfig = { /* ... */ };
|
|
224
|
-
|
|
225
|
-
// Load config on page load
|
|
226
|
-
await IAMService.loadConfig(hybridConfig);
|
|
227
|
-
|
|
228
|
-
// Trigger login when user clicks button
|
|
229
|
-
document.getElementById("login-btn").onclick = () => {
|
|
230
|
-
const returnUrl = new URLSearchParams(window.location.search).get("return") || "/";
|
|
231
|
-
IAMService.doLogin(returnUrl); // returnUrl is where user goes after successful auth
|
|
232
|
-
};
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Callback Page Example
|
|
236
|
-
|
|
237
|
-
```js
|
|
238
|
-
import { IAMService } from "@tidecloak/js";
|
|
239
|
-
|
|
240
|
-
const hybridConfig = { /* same config as login page */ };
|
|
241
|
-
|
|
242
|
-
// initIAM automatically detects the callback (code in URL) and handles token exchange
|
|
243
|
-
const authenticated = await IAMService.initIAM(hybridConfig);
|
|
244
|
-
|
|
245
|
-
if (authenticated) {
|
|
246
|
-
// Success - redirect to original destination
|
|
247
|
-
const returnUrl = IAMService.getReturnUrl() || "/";
|
|
248
|
-
window.location.assign(returnUrl);
|
|
249
|
-
} else {
|
|
250
|
-
// Failed - show error or redirect to login
|
|
251
|
-
document.getElementById("error").textContent = "Login failed. Please try again.";
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Backend Token Exchange
|
|
256
|
-
|
|
257
|
-
Your backend receives a POST request to the configured `tokenExchange.endpoint`:
|
|
258
|
-
|
|
259
|
-
```json
|
|
260
|
-
{
|
|
261
|
-
"accessToken": "{\"code\":\"AUTH_CODE\",\"code_verifier\":\"PKCE_VERIFIER\",\"redirect_uri\":\"https://app.example.com/auth/callback\"}",
|
|
262
|
-
"provider": "tidecloak-auth"
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
Your backend should:
|
|
267
|
-
1. Parse the `accessToken` JSON string
|
|
268
|
-
2. Exchange the code with the IdP's token endpoint
|
|
269
|
-
3. Create a session (e.g., set HTTP-only cookies)
|
|
270
|
-
4. Return a success response
|
|
271
|
-
|
|
272
|
-
### React Example
|
|
273
|
-
|
|
274
|
-
**LoginPage.tsx**
|
|
275
|
-
```tsx
|
|
276
|
-
import { useEffect, useState } from "react";
|
|
277
|
-
import { IAMService } from "@tidecloak/js";
|
|
278
|
-
import { useLocation } from "react-router-dom";
|
|
279
|
-
|
|
280
|
-
const hybridConfig = { /* ... */ };
|
|
281
|
-
|
|
282
|
-
export function LoginPage() {
|
|
283
|
-
const [ready, setReady] = useState(false);
|
|
284
|
-
const location = useLocation();
|
|
285
|
-
const returnUrl = new URLSearchParams(location.search).get("return") || "/";
|
|
286
|
-
|
|
287
|
-
useEffect(() => {
|
|
288
|
-
IAMService.loadConfig(hybridConfig).then(() => setReady(true));
|
|
289
|
-
}, []);
|
|
290
|
-
|
|
291
|
-
return (
|
|
292
|
-
<button disabled={!ready} onClick={() => IAMService.doLogin(returnUrl)}>
|
|
293
|
-
Login with TideCloak
|
|
294
|
-
</button>
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
7
|
```
|
|
298
8
|
|
|
299
|
-
**CallbackPage.tsx**
|
|
300
|
-
```tsx
|
|
301
|
-
import { useEffect, useState } from "react";
|
|
302
|
-
import { IAMService } from "@tidecloak/js";
|
|
303
|
-
|
|
304
|
-
const hybridConfig = { /* same config */ };
|
|
305
|
-
|
|
306
|
-
export function CallbackPage() {
|
|
307
|
-
const [error, setError] = useState<string | null>(null);
|
|
308
|
-
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
IAMService.initIAM(hybridConfig)
|
|
311
|
-
.then(authenticated => {
|
|
312
|
-
if (authenticated) {
|
|
313
|
-
window.location.assign(IAMService.getReturnUrl() || "/");
|
|
314
|
-
} else {
|
|
315
|
-
setError("Login failed");
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
.catch(err => setError(err.message));
|
|
319
|
-
}, []);
|
|
320
|
-
|
|
321
|
-
if (error) return <div>Error: {error}</div>;
|
|
322
|
-
return <div>Logging in...</div>;
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Hybrid Mode Limitations
|
|
327
|
-
|
|
328
|
-
In hybrid mode, tokens are server-side, so these methods will throw:
|
|
329
|
-
- `getToken()`, `getIDToken()`, `getTokenExp()`
|
|
330
|
-
- `getName()`, `hasRealmRole()`, `hasClientRole()`
|
|
331
|
-
- `getValueFromToken()`, `getValueFromIDToken()`
|
|
332
|
-
- `updateIAMToken()`, `forceUpdateToken()`
|
|
333
|
-
- `doEncrypt()`, `doDecrypt()`
|
|
334
|
-
|
|
335
|
-
Use `isLoggedIn()` and `getReturnUrl()` instead.
|
|
336
|
-
|
|
337
9
|
---
|
|
338
10
|
|
|
339
|
-
##
|
|
340
|
-
|
|
341
|
-
TideCloak lets you protect sensitive fields with **tag-based** encryption. You pass in an array of `{ data, tags }` objects and receive an array of encrypted strings (or vice versa for decryption).
|
|
342
|
-
|
|
343
|
-
### Syntax Overview
|
|
344
|
-
|
|
345
|
-
```ts
|
|
346
|
-
// Encrypt one or more payloads:
|
|
347
|
-
const encryptedArray = await doEncrypt([
|
|
348
|
-
{ data: /* any JSON-serializable value */, tags: ['tag1', 'tag2'] },
|
|
349
|
-
]);
|
|
350
|
-
|
|
351
|
-
// Decrypt one or more encrypted blobs:
|
|
352
|
-
const decryptedArray = await doDecrypt([
|
|
353
|
-
{ encrypted: /* string from encrypt() */, tags: ['tag1', 'tag2'] },
|
|
354
|
-
]);
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
> **Important:** The `data` property **must** be either a string or a `Uint8Array` (raw bytes).\
|
|
358
|
-
> When you encrypt a string, decryption returns a string.\
|
|
359
|
-
> When you encrypt a `Uint8Array`, decryption returns a `Uint8Array`.
|
|
360
|
-
|
|
361
|
-
### Valid Example
|
|
362
|
-
>
|
|
363
|
-
> ```ts
|
|
364
|
-
> // Before testing below, ensure you've set up the necessary roles:
|
|
365
|
-
> const multi_encrypted_addresses = await doEncrypt([
|
|
366
|
-
> {
|
|
367
|
-
> data: "10 Smith Street",
|
|
368
|
-
> tags: ["street"]
|
|
369
|
-
> },
|
|
370
|
-
> {
|
|
371
|
-
> data: "Southport",
|
|
372
|
-
> tags: ["suburb"]
|
|
373
|
-
> },
|
|
374
|
-
> {
|
|
375
|
-
> data: "20 James Street - Burleigh Heads",
|
|
376
|
-
> tags: ["street", "suburb"]
|
|
377
|
-
> }
|
|
378
|
-
> ]);
|
|
379
|
-
> ```
|
|
380
|
-
>
|
|
381
|
-
### Invalid (will fail):
|
|
382
|
-
>
|
|
383
|
-
> ```ts
|
|
384
|
-
> // Prepare data for encryption
|
|
385
|
-
> const dataToEncrypt = {
|
|
386
|
-
> title: noteData.title,
|
|
387
|
-
> content: noteData.content
|
|
388
|
-
> };
|
|
389
|
-
>
|
|
390
|
-
> // Encrypt the note data using TideCloak (this will error)
|
|
391
|
-
> const encryptedArray = await doEncrypt([{ data: dataToEncrypt, tags: ['note'] }]);
|
|
392
|
-
> ```
|
|
393
|
-
|
|
394
|
-
* **Permissions:** Encryption requires `_tide_<tag>.selfencrypt`; decryption requires `_tide_<tag>.selfdecrypt`.
|
|
395
|
-
* **Order guarantee:** Output preserves input order.
|
|
11
|
+
## Choose Your Mode
|
|
396
12
|
|
|
13
|
+
| I'm building... | Use this mode |
|
|
14
|
+
|-----------------|---------------|
|
|
15
|
+
| A web app or SPA `(Standard)` | [Front-channel](docs/FRONT_CHANNEL.md) |
|
|
16
|
+
| A secure app where tokens should stay on my server | [Hybrid/BFF](docs/HYBRID_MODE.md) |
|
|
17
|
+
| An Electron, Tauri, or React Native app | [Native](docs/NATIVE_MODE.md) |
|
|
397
18
|
|
|
398
19
|
---
|
|
399
20
|
|
|
400
|
-
|
|
21
|
+
## Quick Comparison
|
|
401
22
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
{ data: '2005-03-04', tags: ['dob'] }
|
|
409
|
-
]);
|
|
410
|
-
|
|
411
|
-
// Multi-field encryption:
|
|
412
|
-
const encryptedFields = await IAMService.doEncrypt([
|
|
413
|
-
{ data: '10 Smith Street', tags: ['street'] },
|
|
414
|
-
{ data: 'Southport', tags: ['suburb'] },
|
|
415
|
-
{ data: '20 James Street – Burleigh Heads', tags: ['street', 'suburb'] }
|
|
416
|
-
]);
|
|
417
|
-
}
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
> **Permissions**: Users need roles matching **every** tag on a payload. A payload tagged `['street','suburb']` requires both the `_tide_street.selfencrypt` and `_tide_suburb.selfencrypt` roles.
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
### Decryption Example
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
import { IAMService } from "@tidecloak/js";
|
|
428
|
-
|
|
429
|
-
async function decryptExamples(encryptedFields) {
|
|
430
|
-
// Single-item decryption:
|
|
431
|
-
const [decryptedDob] = await IAMService.doDecrypt([
|
|
432
|
-
{ encrypted: encryptedFields[0], tags: ['dob'] }
|
|
433
|
-
]);
|
|
434
|
-
|
|
435
|
-
// Multi-field decryption:
|
|
436
|
-
const decryptedFields = await IAMService.doDecrypt([
|
|
437
|
-
{ encrypted: encryptedFields[0], tags: ['street'] },
|
|
438
|
-
{ encrypted: encryptedFields[1], tags: ['suburb'] },
|
|
439
|
-
{ encrypted: encryptedFields[2], tags: ['street','suburb'] }
|
|
440
|
-
]);
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
> **Permissions**: Like encryption, decryption requires the same tag-based roles (`_tide_street.selfdecrypt`, `_tide_suburb.selfdecrypt`, etc.).
|
|
23
|
+
| | Front-channel | Hybrid/BFF | Native |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| Tokens stored in | Browser | Server | App (secure storage) |
|
|
26
|
+
| Best for | Simple web apps | High-security apps | Desktop/mobile apps |
|
|
27
|
+
| Setup complexity | Easy | Medium | Medium |
|
|
28
|
+
| Works offline | No | No | Yes |
|
|
445
29
|
|
|
446
30
|
---
|
|
447
31
|
|
|
448
|
-
##
|
|
449
|
-
|
|
450
|
-
Register handlers via `.on(event, handler)` or remove with `.off(event, handler)`:
|
|
451
|
-
|
|
452
|
-
```js
|
|
453
|
-
IAMService
|
|
454
|
-
.on("logout", () => console.log("User logged out"))
|
|
455
|
-
.on("tokenExpired", () => alert("Session expired, please log in again"));
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
| Event | Emitted When… |
|
|
459
|
-
| -------------------- | ----------------------------------------------------------------------- |
|
|
460
|
-
| `ready` | Initial silent-SSO check completes (handler receives `true` or `false`) |
|
|
461
|
-
| `initError` | Config load or init failure |
|
|
462
|
-
| `authSuccess` | Interactive login succeeded |
|
|
463
|
-
| `authError` | Interactive login failed |
|
|
464
|
-
| `authRefreshSuccess` | Silent token refresh succeeded |
|
|
465
|
-
| `authRefreshError` | Silent token refresh failed |
|
|
466
|
-
| `logout` | User logged out |
|
|
467
|
-
| `tokenExpired` | Token expired before refresh |
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## 9. Core Methods (Front-channel Only)
|
|
472
|
-
|
|
473
|
-
After initialization, you can call these methods anywhere (note: most are only available in front-channel mode):
|
|
474
|
-
|
|
475
|
-
```js
|
|
476
|
-
// Check login state
|
|
477
|
-
IAMService.isLoggedIn(); // boolean
|
|
478
|
-
|
|
479
|
-
// Retrieve tokens
|
|
480
|
-
await IAMService.getToken(); // string (access token)
|
|
481
|
-
IAMService.getIDToken(); // string (ID token)
|
|
482
|
-
|
|
483
|
-
// Inspect token metadata
|
|
484
|
-
IAMService.getTokenExp(); // seconds until expiry
|
|
485
|
-
IAMService.getName(); // preferred_username claim
|
|
486
|
-
|
|
487
|
-
// Role checks
|
|
488
|
-
IAMService.hasRealmRole("admin"); // boolean
|
|
489
|
-
IAMService.hasClientRole("editor"); // boolean
|
|
490
|
-
|
|
491
|
-
// Custom claims
|
|
492
|
-
IAMService.getValueFromToken("foo"); // any
|
|
493
|
-
IAMService.getValueFromIDToken("bar");// any
|
|
494
|
-
|
|
495
|
-
// Force a token update
|
|
496
|
-
await IAMService.updateIAMToken(); // boolean (whether refreshed)
|
|
497
|
-
await IAMService.forceUpdateToken(); // boolean
|
|
498
|
-
|
|
499
|
-
// Programmatic login / logout
|
|
500
|
-
IAMService.doLogin(); // redirects to SSO
|
|
501
|
-
IAMService.doLogout(); // clears cookie & redirects
|
|
502
|
-
|
|
503
|
-
// Data encryption / decryption (TideCloak service)
|
|
504
|
-
await IAMService.doEncrypt([{ data: "secret", tags: ["tag1"] }]);
|
|
505
|
-
await IAMService.doDecrypt([{ encrypted: "...", tags: ["tag1"] }]);
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
---
|
|
509
|
-
|
|
510
|
-
## 10. Tips & Best Practices
|
|
511
|
-
|
|
512
|
-
* **Single Init**: Call `initIAM` only once on page load or app bootstrap.
|
|
513
|
-
* **Token Cookie**: `kcToken` is set automatically; ensure server-side middleware reads this cookie.
|
|
514
|
-
* **Error Handling**: Listen to `initError` and `authError` to gracefully recover.
|
|
515
|
-
* **Silent Refresh**: Built-in; you only need to call `updateIAMToken` if you want manual control.
|
|
516
|
-
* **Event Cleanup**: Use `.off(...)` in SPAs before component unmount.
|
|
517
|
-
* **Redirect URI**: If using a custom `redirectUri`, ensure the route or file exists.
|
|
518
|
-
|
|
519
|
-
---
|
|
32
|
+
## Requirements
|
|
520
33
|
|
|
34
|
+
- A TideCloak server ([setup guide](https://github.com/tide-foundation/tidecloak-gettingstarted))
|
|
35
|
+
- A registered client in your TideCloak realm
|