@tidecloak/react 0.9.11 → 0.9.13

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 CHANGED
@@ -1,23 +1,25 @@
1
1
  # TideCloak React SDK
2
2
 
3
- Secure your React app with TideCloak: authentication, session management, and data encryption—all in minutes.
3
+ Secure your React app with TideCloak: authentication, session management, data encryption, and role-based access.
4
4
 
5
5
  ---
6
6
 
7
7
  ## 1. Prerequisites
8
8
 
9
- Before you begin, ensure you have:
9
+ Before you begin, ensure you have the following:
10
10
 
11
- - React 18 or later
12
- - Node.js >=18.17.0 or later
13
- - A running TideCloak server.
14
- - A registered client in your realm.
11
+ * **React 18** or later
12
+ * **Node.js 18.17.0**
13
+ * A [running](https://github.com/tide-foundation/tidecloak-gettingstarted) TideCloak server you have admin control over
14
+ * IGA enabled realm
15
+ * A registered client in your realm with default user contexts approved and committed
16
+ * A valid Keycloak adapter JSON file (e.g., `tidecloakAdapter.json`)
15
17
 
16
18
  ---
17
19
 
18
- ## 2. Install the SDK
20
+ ## 2. Install `@tidecloak/react`
19
21
 
20
- Add the React package to your project:
22
+ Add the TideCloak React SDK to your project:
21
23
 
22
24
  ```bash
23
25
  npm install @tidecloak/react
@@ -27,41 +29,121 @@ yarn add @tidecloak/react
27
29
 
28
30
  This bundle provides:
29
31
 
30
- - `<TideCloakProvider>` — application-level context.
31
- - `useTideCloak()` hook — access tokens and auth actions.
32
- - `<Authenticated>` / `<Unauthenticated>` — UI guards.
32
+ * `<TideCloakProvider>` — application-level context
33
+ * `useTideCloak()` hook — access tokens and auth actions
34
+ * `<Authenticated>` / `<Unauthenticated>` — UI guards
35
+ * `doEncrypt()` / `doDecrypt()` — tag-based encryption/decryption
33
36
 
34
37
  ---
35
38
 
36
39
  ## 3. Initialize the Provider
37
40
 
38
- Wrap your root component in `<TideCloakProvider>` to load adapter settings and bootstrap auth:
41
+ Wrap your app’s root with `<TideCloakProvider>` to enable authentication context throughout the component tree.
42
+
43
+ If you're using React Router, your setup might look like this:
44
+
45
+ **File:** `src/App.tsx`
39
46
 
40
47
  ```tsx
41
48
  import React from 'react';
49
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
42
50
  import { TideCloakProvider } from '@tidecloak/react';
43
51
  import adapter from '../tidecloakAdapter.json';
52
+ import Home from './pages/Home';
53
+ import RedirectPage from './pages/auth/RedirectPage';
44
54
 
45
55
  export default function App() {
46
56
  return (
47
57
  <TideCloakProvider config={adapter}>
48
- <YourApp />
58
+ <BrowserRouter>
59
+ <Routes>
60
+ <Route path="/" element={<Home />} />
61
+ <Route path="/auth/redirect" element={<RedirectPage />} />
62
+ {/* Add additional routes here */}
63
+ </Routes>
64
+ </BrowserRouter>
49
65
  </TideCloakProvider>
50
66
  );
51
67
  }
52
68
  ```
53
69
 
54
- **What it does:**
70
+ > ⚠️ If you don't define a route at `/auth/redirect`, and you're using the default `redirectUri`, your app **will break after login/logout**. Either create this route or override `redirectUri` in the provider config.
71
+ >
72
+ > If you override the `redirectUri`, you **must** ensure that the custom path exists in your router. Otherwise, the app will redirect to a non-existent route and fail.
73
+
74
+ ---
75
+
76
+ ## 4. Redirect URI Handling
77
+
78
+ TideCloak supports an optional `redirectUri` config field. This defines where the user is sent after login/logout.
79
+
80
+ If omitted, it defaults to:
81
+
82
+ ```ts
83
+ `${window.location.origin}/auth/redirect`
84
+ ```
85
+
86
+ > Example: If your app runs at `http://localhost:3000`, then by default the redirect path is `http://localhost:3000/auth/redirect`.
55
87
 
56
- - **Loads** your adapter JSON.
57
- - **Initializes** internal auth flows and listeners.
58
- - **Provides** auth state & methods via React Context.
88
+ You must **create this route** if you use the default, or explicitly override it:
89
+
90
+ ```tsx
91
+ <TideCloakProvider config={{ ...adapter, redirectUri: 'https://yourapp.com/auth/callback' }}>
92
+ <YourApp />
93
+ </TideCloakProvider>
94
+ ```
95
+
96
+ > ⚠️ If you override the `redirectUri`, make sure the specified path exists in your app. Missing this route will cause failed redirects.
97
+
98
+ ### Example: Redirect Handling Page
99
+
100
+ **File:** `src/pages/auth/RedirectPage.tsx`
101
+
102
+ ```tsx
103
+ import { useEffect } from 'react';
104
+ import { useNavigate } from 'react-router-dom';
105
+ import { useTideCloak } from '@tidecloak/react';
106
+
107
+ export default function RedirectPage() {
108
+ const { authenticated, isInitializing, logout } = useTideCloak();
109
+ const navigate = useNavigate();
110
+
111
+ useEffect(() => {
112
+ const params = new URLSearchParams(window.location.search);
113
+ if (params.get("auth") === "failed") {
114
+ sessionStorage.setItem("tokenExpired", "true");
115
+ logout();
116
+ }
117
+ }, []);
118
+
119
+ useEffect(() => {
120
+ if (!isInitializing) {
121
+ navigate(authenticated ? '/home' : '/');
122
+ }
123
+ }, [authenticated, isInitializing, navigate]);
124
+
125
+ return (
126
+ <div style={{
127
+ minHeight: '100vh',
128
+ display: 'flex',
129
+ alignItems: 'center',
130
+ justifyContent: 'center',
131
+ fontSize: '1rem',
132
+ color: '#555',
133
+ }}>
134
+ <p>Waiting for authentication...</p>
135
+ </div>
136
+ );
137
+ }
138
+ ```
139
+
140
+ **Description:** This page helps finalize the login or logout flow, and also reacts to token expiration events that may have triggered a redirect. It's required if you're using the default `redirectUri`. If you override the redirect URI, the file is optional—but the **route** for the redirect **must** still exist in your app.
59
141
 
60
142
  ---
61
143
 
62
- ## 4. Using the `useTideCloak` Hook
144
+ ## 5. Using the `useTideCloak` Hook
63
145
 
64
- Use this hook anywhere to manage auth:
146
+ Use this hook anywhere in your component tree to manage authentication:
65
147
 
66
148
  ```tsx
67
149
  import { useTideCloak } from '@tidecloak/react';
@@ -78,6 +160,8 @@ function Header() {
78
160
  getValueFromIdToken,
79
161
  hasRealmRole,
80
162
  hasClientRole,
163
+ doEncrypt,
164
+ doDecrypt,
81
165
  } = useTideCloak();
82
166
 
83
167
  return (
@@ -98,26 +182,24 @@ function Header() {
98
182
  }
99
183
  ```
100
184
 
101
- **Key methods & props:**
102
-
103
- | Name | Type | Description |
104
- | ---------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------- |
105
- | `authenticated` | `boolean` | Whether the user is logged in. |
106
- | `login()` / `logout()` | `() => void` | Trigger the login or logout flows. |
107
- | `token`, `tokenExp` | `string`, `number` | Access token and its expiration timestamp. |
108
- | Automatic token refresh | built-in | Tokens refresh silently on expiration—no manual setup needed. |
109
- | `refreshToken()` | `() => Promise<boolean>` | Force a silent token renewal. |
110
- | `getValueFromToken(key)` | `(key: string) => any` | Read a custom claim from the access token. |
111
- | `getValueFromIdToken(key)` | `(key: string) => any` | Read a custom claim from the ID token. |
112
- | `hasRealmRole(role)` | `(role: string) => boolean` | Check a realm-level role. |
113
- | `hasClientRole(role, client?)` | `(role: string, client?: string) => boolean` | Check a client-level role; defaults to your app’s client ID if omitted. |
114
- | `doEncrypt(data)``doDecrypt(data)` | `(data: any) => Promise<any>` | Encrypt or decrypt payloads via TideCloak’s built-in service. |
185
+ | Name | Type | Description |
186
+ | ------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------- |
187
+ | `authenticated` | `boolean` | Whether the user is logged in. |
188
+ | `login()` / `logout()` | `() => void` | Trigger the login or logout flows. |
189
+ | `token`, `tokenExp` | `string`, `number` | Access token and its expiration timestamp. |
190
+ | Automatic token refresh | built-in | Tokens refresh silently on expiration—no manual setup needed. |
191
+ | `refreshToken()` | `() => Promise<boolean>` | Force a silent token renewal. |
192
+ | `getValueFromToken(key)` | `(key: string) => any` | Read a custom claim from the access token. |
193
+ | `getValueFromIdToken(key)` | `(key: string) => any` | Read a custom claim from the ID token. |
194
+ | `hasRealmRole(role)` | `(role: string) => boolean` | Check a realm-level role. |
195
+ | `hasClientRole(role, client?)` | `(role: string, client?: string) => boolean` | Check a client-level role; defaults to your app’s client ID if omitted. |
196
+ | `doEncrypt(data)` / `doDecrypt(data)` | `(data: any) => Promise<any>` | Encrypt or decrypt payloads via TideCloak’s built-in service. |
115
197
 
116
198
  ---
117
199
 
118
- ## 5. Guard Components
200
+ ## 6. Guard Components
119
201
 
120
- Use out-of-the-box components to show or hide content:
202
+ Use these components to show or hide content based on authentication state:
121
203
 
122
204
  ```tsx
123
205
  import { Authenticated, Unauthenticated } from '@tidecloak/react';
@@ -138,93 +220,38 @@ function Dashboard() {
138
220
  }
139
221
  ```
140
222
 
141
- - `<Authenticated>`: renders children only when `authenticated === true`.
142
- - `<Unauthenticated>`: renders children only when `authenticated === false`.
223
+ * `<Authenticated>`: renders children only when `authenticated === true`
224
+ * `<Unauthenticated>`: renders children only when `authenticated === false`
143
225
 
144
226
  ---
145
227
 
146
- ## 6. Encrypting & Decrypting Data
228
+ ## 7. Encrypting & Decrypting Data
147
229
 
148
- 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).
149
-
150
- ### Syntax Overview
230
+ Protect sensitive payloads using tag-based encryption/decryption:
151
231
 
152
232
  ```ts
153
- // Encrypt one or more payloads:
154
- const encryptedArray: string[] = await doEncrypt([
155
- { data: /* any JSON-serializable value */, tags: ['tag1', 'tag2'] },
156
- // …
233
+ // Encrypt:
234
+ const encryptedArray = await doEncrypt([
235
+ { data: { email: 'user@example.com' }, tags: ['email'] },
157
236
  ]);
158
237
 
159
- // Decrypt one or more encrypted blobs:
160
- const decryptedArray: any[] = await doDecrypt([
161
- { encrypted: /* string from encrypt() */, tags: ['tag1', 'tag2'] },
162
- // …
238
+ // Decrypt:
239
+ const decryptedArray = await doDecrypt([
240
+ { encrypted: encryptedArray[0], tags: ['email'] },
163
241
  ]);
164
242
  ```
165
243
 
166
- > **Order guarantee**: the returned array matches the input order.
167
-
168
- ---
169
-
170
- ### Encryption Example
171
-
172
- ```tsx
173
- import { useTideCloak } from '@tidecloak/react';
174
-
175
- async function encryptExamples() {
176
- const { doEncrypt } = useTideCloak();
177
-
178
- // Simple single-item encryption:
179
- const [encryptedDob] = await doEncrypt([
180
- { data: '2005-03-04', tags: ['dob'] }
181
- ]);
182
-
183
- // Multi-field encryption:
184
- const encryptedFields = await doEncrypt([
185
- { data: '10 Smith Street', tags: ['street'] },
186
- { data: 'Southport', tags: ['suburb'] },
187
- { data: { full: '20 James Street – Burleigh Heads' }, tags: ['street', 'suburb'] }
188
- ]);
189
- }
190
- ```
191
-
192
- > **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.
244
+ > **Permissions**: Encryption requires `tide_<tag>.selfencrypt`; decryption requires `tide_<tag>.selfdecrypt`.
245
+ > **Order guarantee**: Output preserves input order.
193
246
 
194
247
  ---
195
248
 
196
- ### Decryption Example
249
+ ## 8. Advanced & Best Practices
197
250
 
198
- ```tsx
199
- import { useTideCloak } from '@tidecloak/react';
200
-
201
- async function decryptExamples(encryptedFields: string[]) {
202
- const { doDecrypt } = useTideCloak();
203
-
204
- // Single-item decryption:
205
- const [decryptedDob] = await doDecrypt([
206
- { encrypted: encryptedFields[0], tags: ['dob'] }
207
- ]);
208
-
209
- // Multi-field decryption:
210
- const decryptedFields = await doDecrypt([
211
- { encrypted: encryptedFields[0], tags: ['street'] },
212
- { encrypted: encryptedFields[1], tags: ['suburb'] },
213
- { encrypted: encryptedFields[2], tags: ['street','suburb'] }
214
- ]);
215
- }
216
- ```
217
-
218
- > **Permissions**: Like encryption, decryption requires the same tag-based roles (`tide_street.selfdecrypt`, `tide_suburb.selfdecrypt`, etc.).
251
+ * **Auto-Refresh**: built-in, no manual timer setup required
252
+ * **Error Handling**: check `initError` from `useTideCloak`
253
+ * **Custom Claims**: access token fields with `getValueFromToken()` / `getValueFromIdToken()`
254
+ * **Role-Based UI**: combine `hasRealmRole`, `hasClientRole`, and guard components
255
+ * **Lazy Initialization**: optionally wrap `<TideCloakProvider>` around only protected routes
219
256
 
220
257
  ---
221
-
222
- ## 7. Advanced & Best Practices
223
-
224
- - **Auto-Refresh**: built into the provider—no manual token timers needed.
225
- - **Error Handling**: use the `initError` value from `useTideCloak` to catch startup issues.
226
- - **Custom Claims**: store app-specific data in JWT claims and access via `getValueFromToken()` / `getValueFromIdToken()`.
227
- - **Role-Based Access**: combine `hasRealmRole` and `hasClientRole` with guard components for fine-grained control.
228
- - **Lazy Initialization**: wrap `<TideCloakProvider>` around only authenticated sections in large apps.
229
-
230
- ---
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { TideCloakContextProvider } from "./TideCloakContextProvider";
3
+ export const TideCloakProvider = ({ config, children }) => (_jsx(TideCloakContextProvider, { config: config, children: children }));
package/dist/cjs/index.js CHANGED
@@ -1,10 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { TideCloakContextProvider, useTideCloakContext, } from './contexts/TideCloakContextProvider';
3
- /**
4
- * Wrap your app and bootstrap TideCloak with the given config object.
5
- * tidecloak-js expects a plain JSON config, so we type config as Record<string, any>.
6
- */
7
- export const TideCloakProvider = ({ config, children }) => (_jsx(TideCloakContextProvider, { config: config, children: children }));
1
+ import { useTideCloakContext, } from './contexts/TideCloakContextProvider';
2
+ export { TideCloakContextProvider } from './contexts/TideCloakContextProvider';
8
3
  /**
9
4
  * Hook to access authentication state and helpers.
10
5
  */
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { TideCloakContextProvider } from "./TideCloakContextProvider";
3
+ export const TideCloakProvider = ({ config, children }) => (_jsx(TideCloakContextProvider, { config: config, children: children }));
package/dist/esm/index.js CHANGED
@@ -1,10 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { TideCloakContextProvider, useTideCloakContext, } from './contexts/TideCloakContextProvider';
3
- /**
4
- * Wrap your app and bootstrap TideCloak with the given config object.
5
- * tidecloak-js expects a plain JSON config, so we type config as Record<string, any>.
6
- */
7
- export const TideCloakProvider = ({ config, children }) => (_jsx(TideCloakContextProvider, { config: config, children: children }));
1
+ import { useTideCloakContext, } from './contexts/TideCloakContextProvider';
2
+ export { TideCloakContextProvider } from './contexts/TideCloakContextProvider';
8
3
  /**
9
4
  * Hook to access authentication state and helpers.
10
5
  */
@@ -0,0 +1,8 @@
1
+ import { ReactNode, FC } from "react";
2
+ interface TideCloakProviderProps {
3
+ config: Record<string, any>;
4
+ children: ReactNode;
5
+ }
6
+ export declare const TideCloakProvider: FC<TideCloakProviderProps>;
7
+ export {};
8
+ //# sourceMappingURL=TideCloakProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TideCloakProvider.d.ts","sourceRoot":"","sources":["../../../src/contexts/TideCloakProvider.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAG7C,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAIxD,CAAC"}
@@ -1,13 +1,6 @@
1
- import React, { ReactNode, FC } from 'react';
1
+ import React from 'react';
2
2
  import { useTideCloakContext } from './contexts/TideCloakContextProvider';
3
- /**
4
- * Wrap your app and bootstrap TideCloak with the given config object.
5
- * tidecloak-js expects a plain JSON config, so we type config as Record<string, any>.
6
- */
7
- export declare const TideCloakProvider: FC<{
8
- config: Record<string, any>;
9
- children: ReactNode;
10
- }>;
3
+ export { TideCloakContextProvider } from './contexts/TideCloakContextProvider';
11
4
  /**
12
5
  * Hook to access authentication state and helpers.
13
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAEL,mBAAmB,EACpB,MAAM,qCAAqC,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAKpF,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,YAAY,4BAAsB,CAAC;AAEhD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAI1F;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAI5F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACpB,MAAM,qCAAqC,CAAC;AAE7C,OAAQ,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,YAAY,4BAAsB,CAAC;AAEhD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAI1F;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAI5F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tidecloak/react",
3
- "version": "0.9.11",
3
+ "version": "0.9.13",
4
4
  "description": "TideCloak client-side React SDK",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -48,6 +48,6 @@
48
48
  "@types/react-dom": "^19.1.6"
49
49
  },
50
50
  "dependencies": {
51
- "@tidecloak/js": "^0.9.11"
51
+ "@tidecloak/js": "^0.9.13"
52
52
  }
53
53
  }