@tidecloak/react 0.9.11 → 0.9.12
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 +135 -108
- package/dist/cjs/contexts/TideCloakProvider.js +3 -0
- package/dist/cjs/index.js +2 -7
- package/dist/esm/contexts/TideCloakProvider.js +3 -0
- package/dist/esm/index.js +2 -7
- package/dist/types/contexts/TideCloakProvider.d.ts +8 -0
- package/dist/types/contexts/TideCloakProvider.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -9
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
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,
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
20
|
+
## 2. Install `@tidecloak/react`
|
|
19
21
|
|
|
20
|
-
Add the React
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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
|
-
<
|
|
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
|
-
**
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
##
|
|
144
|
+
## 5. Using the `useTideCloak` Hook
|
|
63
145
|
|
|
64
|
-
Use this hook anywhere to manage
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
| `
|
|
106
|
-
|
|
|
107
|
-
| `
|
|
108
|
-
|
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
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
|
-
##
|
|
200
|
+
## 6. Guard Components
|
|
119
201
|
|
|
120
|
-
Use
|
|
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
|
-
|
|
142
|
-
|
|
223
|
+
* `<Authenticated>`: renders children only when `authenticated === true`
|
|
224
|
+
* `<Unauthenticated>`: renders children only when `authenticated === false`
|
|
143
225
|
|
|
144
226
|
---
|
|
145
227
|
|
|
146
|
-
##
|
|
228
|
+
## 7. Encrypting & Decrypting Data
|
|
147
229
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
### Syntax Overview
|
|
230
|
+
Protect sensitive payloads using tag-based encryption/decryption:
|
|
151
231
|
|
|
152
232
|
```ts
|
|
153
|
-
// Encrypt
|
|
154
|
-
const encryptedArray
|
|
155
|
-
{ data:
|
|
156
|
-
// …
|
|
233
|
+
// Encrypt:
|
|
234
|
+
const encryptedArray = await doEncrypt([
|
|
235
|
+
{ data: { email: 'user@example.com' }, tags: ['email'] },
|
|
157
236
|
]);
|
|
158
237
|
|
|
159
|
-
// Decrypt
|
|
160
|
-
const decryptedArray
|
|
161
|
-
{ encrypted:
|
|
162
|
-
// …
|
|
238
|
+
// Decrypt:
|
|
239
|
+
const decryptedArray = await doDecrypt([
|
|
240
|
+
{ encrypted: encryptedArray[0], tags: ['email'] },
|
|
163
241
|
]);
|
|
164
242
|
```
|
|
165
243
|
|
|
166
|
-
> **
|
|
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
|
-
|
|
249
|
+
## 8. Advanced & Best Practices
|
|
197
250
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
---
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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
|
*/
|
package/dist/esm/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import 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,
|
|
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.
|
|
3
|
+
"version": "0.9.12",
|
|
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.
|
|
51
|
+
"@tidecloak/js": "^0.9.12"
|
|
52
52
|
}
|
|
53
53
|
}
|