spry-apps-dropdown 3.0.1 → 3.0.2
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 +275 -276
- package/dist/index.cjs +15 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1349 -1332
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,71 +1,17 @@
|
|
|
1
1
|
# spry-apps-dropdown
|
|
2
|
-
## Integration Guide
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
This package requires OIDC authentication. Use `react-oidc-context` or `@react-keycloak/web` for best results.
|
|
7
|
-
|
|
8
|
-
**Example OIDC config:**
|
|
9
|
-
|
|
10
|
-
```tsx
|
|
11
|
-
const oidcConfig = {
|
|
12
|
-
authority: 'https://auth.sprylogin.com/realms/sprylogin',
|
|
13
|
-
client_id: 'my-app-client',
|
|
14
|
-
redirect_uri: window.location.origin,
|
|
15
|
-
post_logout_redirect_uri: window.location.origin,
|
|
16
|
-
onSigninCallback: () => {
|
|
17
|
-
window.history.replaceState({}, document.title, window.location.pathname)
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### 2. Multi-Account Management
|
|
23
|
-
|
|
24
|
-
The package supports multi-account switching, add-account, and soft logout. All account state is stored in localStorage for cross-app/iframe sync.
|
|
25
|
-
|
|
26
|
-
**Syncing across apps/iframe:**
|
|
27
|
-
- Use the same localStorage key (`spry_accounts`) in all apps.
|
|
28
|
-
- When an account is added, removed, or switched, update localStorage and reload state in all apps.
|
|
29
|
-
- For iframe/bridge integration, use a shared localStorage or a custom syncBridge client.
|
|
30
|
-
|
|
31
|
-
### 3. Add Another Account Flow
|
|
32
|
-
|
|
33
|
-
When adding another account:
|
|
34
|
-
- A flag (`spry_add_account_pending`) is set in localStorage.
|
|
35
|
-
- After soft logout, the app checks this flag and triggers OIDC login for the new account.
|
|
36
|
-
- On callback, the new account is added and set as active.
|
|
37
|
-
|
|
38
|
-
### 4. Cross-App Sync
|
|
39
|
-
|
|
40
|
-
To sync active account and account removal across apps:
|
|
41
|
-
- Listen for changes to `spry_accounts` in localStorage (using the `storage` event or polling).
|
|
42
|
-
- When the accounts state changes, update your app's state and UI.
|
|
43
|
-
- If using an iframe bridge, ensure the bridge propagates account changes to all connected apps.
|
|
44
|
-
|
|
45
|
-
### 5. Troubleshooting
|
|
46
|
-
|
|
47
|
-
- If new accounts are not added after login, ensure your OIDC provider updates the user context before the callback runs.
|
|
48
|
-
- If cross-app sync is not working, check that all apps use the same localStorage key and listen for changes.
|
|
49
|
-
- For CORS or redirect issues, verify your OIDC config and Keycloak setup.
|
|
50
|
-
|
|
51
|
-
### 6. Example Integration
|
|
52
|
-
|
|
53
|
-
See the Quick Start and Usage sections below for full code examples.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
A React component library for displaying Spry apps dropdown and profile menu with dynamic API integration. Features a Google-style UI with beautiful animations.
|
|
3
|
+
A React component library for Spry apps dropdown and profile menu with **multi-account switching**. Features a Google-style UI with beautiful animations and automatic Keycloak authentication.
|
|
57
4
|
|
|
58
5
|
## Features
|
|
59
6
|
|
|
60
7
|
- 🎨 Beautiful Material-UI design with smooth animations (Google-inspired)
|
|
61
|
-
-
|
|
62
|
-
-
|
|
8
|
+
- 👥 **Multi-account switching** - Users can log into multiple accounts and switch between them
|
|
9
|
+
- 🔄 Automatic data fetching from API with caching
|
|
10
|
+
- 🔐 Built-in Keycloak/OIDC authentication via react-oidc-context
|
|
63
11
|
- ⚡ Loading and error states
|
|
64
12
|
- 📱 Responsive layout
|
|
65
13
|
- 🎯 TypeScript support
|
|
66
|
-
- 🔌 Easy integration
|
|
67
|
-
- 👤 Profile menu with avatar, account management, and sign out (always shows Manage your Account)
|
|
68
|
-
- 🎛️ Combined TopBar component with both apps dropdown and profile menu
|
|
14
|
+
- 🔌 Easy integration - Just 3 steps!
|
|
69
15
|
|
|
70
16
|
## Installation
|
|
71
17
|
|
|
@@ -73,31 +19,33 @@ A React component library for displaying Spry apps dropdown and profile menu wit
|
|
|
73
19
|
npm install spry-apps-dropdown react-oidc-context oidc-client-ts
|
|
74
20
|
```
|
|
75
21
|
|
|
76
|
-
|
|
22
|
+
**Peer Dependencies:**
|
|
23
|
+
- `react` ^18.0.0 || ^19.0.0
|
|
24
|
+
- `react-dom` ^18.0.0 || ^19.0.0
|
|
25
|
+
- `react-oidc-context` ^3.3.0
|
|
26
|
+
- `oidc-client-ts` ^1.0.0
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start (3 Steps)
|
|
77
31
|
|
|
78
|
-
|
|
32
|
+
### Step 1: Wrap Your App with `SpryAuthProvider`
|
|
79
33
|
|
|
80
34
|
```tsx
|
|
35
|
+
// main.tsx or index.tsx
|
|
81
36
|
import React from 'react'
|
|
82
37
|
import ReactDOM from 'react-dom/client'
|
|
83
|
-
import { SpryAuthProvider
|
|
38
|
+
import { SpryAuthProvider } from 'spry-apps-dropdown'
|
|
39
|
+
import App from './App'
|
|
84
40
|
|
|
85
41
|
const oidcConfig = {
|
|
86
42
|
authority: 'https://auth.sprylogin.com/realms/sprylogin',
|
|
87
|
-
client_id: '
|
|
43
|
+
client_id: 'your-client-id',
|
|
88
44
|
redirect_uri: window.location.origin,
|
|
89
45
|
post_logout_redirect_uri: window.location.origin,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const accountManager = useSpryAccountManager()
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<TopBar
|
|
97
|
-
apiUrl="https://sprylogin.com/apps-api"
|
|
98
|
-
accountManager={accountManager}
|
|
99
|
-
/>
|
|
100
|
-
)
|
|
46
|
+
onSigninCallback: () => {
|
|
47
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
48
|
+
},
|
|
101
49
|
}
|
|
102
50
|
|
|
103
51
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
@@ -109,253 +57,252 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
109
57
|
)
|
|
110
58
|
```
|
|
111
59
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
### TopBar Component (Easiest - Recommended for v2.0+)
|
|
115
|
-
|
|
116
|
-
The `TopBar` component combines both apps dropdown and profile menu in a single component, just like Google's interface:
|
|
60
|
+
### Step 2: Use `useSpryAccountManager` Hook
|
|
117
61
|
|
|
118
62
|
```tsx
|
|
119
|
-
|
|
63
|
+
// App.tsx
|
|
64
|
+
import { useSpryAccountManager } from 'spry-apps-dropdown'
|
|
65
|
+
|
|
66
|
+
function App() {
|
|
67
|
+
const accountManager = useSpryAccountManager()
|
|
120
68
|
|
|
121
|
-
function MyAppBar() {
|
|
122
69
|
return (
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
My App
|
|
127
|
-
</Typography>
|
|
128
|
-
|
|
129
|
-
<TopBar
|
|
130
|
-
apiUrl="https://your-api.com"
|
|
131
|
-
onSignOut={() => handleLogout()}
|
|
132
|
-
getAuthToken={() => yourAuthToken}
|
|
133
|
-
appsRefetchInterval={30000} // 30 seconds
|
|
134
|
-
appsCacheTime={30000}
|
|
135
|
-
profileRefetchInterval={5 * 60 * 1000} // 5 minutes
|
|
136
|
-
profileCacheTime={5 * 60 * 1000}
|
|
137
|
-
/>
|
|
138
|
-
</Toolbar>
|
|
139
|
-
</AppBar>
|
|
70
|
+
<div>
|
|
71
|
+
{/* Your app content */}
|
|
72
|
+
</div>
|
|
140
73
|
)
|
|
141
74
|
}
|
|
142
75
|
```
|
|
143
76
|
|
|
144
|
-
###
|
|
145
|
-
|
|
146
|
-
Use just the profile menu component:
|
|
77
|
+
### Step 3: Add the TopBar Component
|
|
147
78
|
|
|
148
79
|
```tsx
|
|
149
|
-
|
|
80
|
+
// App.tsx
|
|
81
|
+
import { TopBar, useSpryAuth, useSpryAccountManager } from 'spry-apps-dropdown'
|
|
82
|
+
|
|
83
|
+
function App() {
|
|
84
|
+
const auth = useSpryAuth()
|
|
85
|
+
const accountManager = useSpryAccountManager()
|
|
150
86
|
|
|
151
|
-
function MyComponent() {
|
|
152
87
|
return (
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
88
|
+
<div>
|
|
89
|
+
<AppBar>
|
|
90
|
+
<Toolbar>
|
|
91
|
+
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
|
92
|
+
My App
|
|
93
|
+
</Typography>
|
|
94
|
+
|
|
95
|
+
<TopBar
|
|
96
|
+
apiUrl="https://sprylogin.com/apps-api"
|
|
97
|
+
onSignOut={() => auth.signoutRedirect()}
|
|
98
|
+
getAuthToken={() => auth.user?.access_token || null}
|
|
99
|
+
accountManager={accountManager}
|
|
100
|
+
/>
|
|
101
|
+
</Toolbar>
|
|
102
|
+
</AppBar>
|
|
103
|
+
|
|
104
|
+
{/* Your app content */}
|
|
105
|
+
</div>
|
|
160
106
|
)
|
|
161
107
|
}
|
|
162
108
|
```
|
|
163
109
|
|
|
164
|
-
|
|
110
|
+
**That's it!** Your app now has:
|
|
111
|
+
- ✅ Multi-account switching
|
|
112
|
+
- ✅ Apps dropdown
|
|
113
|
+
- ✅ Profile menu
|
|
114
|
+
- ✅ Automatic authentication
|
|
115
|
+
|
|
116
|
+
---
|
|
165
117
|
|
|
166
|
-
|
|
118
|
+
## Using Auth Token in Your API Calls
|
|
119
|
+
|
|
120
|
+
Get the current user's token using `useSpryAuth()`:
|
|
167
121
|
|
|
168
122
|
```tsx
|
|
169
|
-
import {
|
|
170
|
-
import { IconButton } from '@mui/material'
|
|
171
|
-
import { Apps } from '@mui/icons-material'
|
|
172
|
-
import { AppsDropdownConnected } from 'spry-apps-dropdown'
|
|
123
|
+
import { useSpryAuth } from 'spry-apps-dropdown'
|
|
173
124
|
|
|
174
125
|
function MyComponent() {
|
|
175
|
-
const
|
|
176
|
-
const appsButtonRef = useRef<HTMLButtonElement>(null)
|
|
126
|
+
const auth = useSpryAuth()
|
|
177
127
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<AppsDropdownConnected
|
|
188
|
-
apiUrl="https://sprylogin.com/apps-api"
|
|
189
|
-
open={appsMenuOpen}
|
|
190
|
-
onClose={() => setAppsMenuOpen(false)}
|
|
191
|
-
buttonRef={appsButtonRef}
|
|
192
|
-
/>
|
|
193
|
-
</>
|
|
194
|
-
)
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const token = auth.user?.access_token
|
|
130
|
+
|
|
131
|
+
fetch('/api/data', {
|
|
132
|
+
headers: {
|
|
133
|
+
'Authorization': `Bearer ${token}`
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}, [auth.user])
|
|
195
137
|
}
|
|
196
138
|
```
|
|
197
139
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Use `AppsDropdown` directly with custom data source:
|
|
140
|
+
**For React Query:**
|
|
201
141
|
|
|
202
142
|
```tsx
|
|
203
|
-
import {
|
|
143
|
+
import { useSpryAuth } from 'spry-apps-dropdown'
|
|
144
|
+
import { useQuery } from '@tanstack/react-query'
|
|
204
145
|
|
|
205
146
|
function MyComponent() {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
147
|
+
const auth = useSpryAuth()
|
|
148
|
+
|
|
149
|
+
const { data } = useQuery({
|
|
150
|
+
queryKey: ['data'],
|
|
151
|
+
queryFn: async () => {
|
|
152
|
+
const token = auth.user?.access_token
|
|
153
|
+
const res = await fetch('/api/data', {
|
|
154
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
155
|
+
})
|
|
156
|
+
return res.json()
|
|
157
|
+
},
|
|
158
|
+
enabled: !!auth.user,
|
|
213
159
|
})
|
|
214
|
-
|
|
215
|
-
return (
|
|
216
|
-
<>
|
|
217
|
-
<IconButton ref={appsButtonRef} onClick={() => setAppsMenuOpen(!appsMenuOpen)}>
|
|
218
|
-
<Apps />
|
|
219
|
-
</IconButton>
|
|
220
|
-
|
|
221
|
-
<AppsDropdown
|
|
222
|
-
apps={apps}
|
|
223
|
-
open={appsMenuOpen}
|
|
224
|
-
onClose={() => setAppsMenuOpen(false)}
|
|
225
|
-
buttonRef={appsButtonRef}
|
|
226
|
-
isLoading={isLoading}
|
|
227
|
-
error={error}
|
|
228
|
-
onRetry={refetch}
|
|
229
|
-
/>
|
|
230
|
-
</>
|
|
231
|
-
)
|
|
232
160
|
}
|
|
233
161
|
```
|
|
234
162
|
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Multi-Account Switching
|
|
166
|
+
|
|
167
|
+
The package automatically handles multi-account switching:
|
|
168
|
+
|
|
169
|
+
1. **User clicks "Add Another Account"** in ProfileMenu
|
|
170
|
+
2. **Redirects to Keycloak** for login (with `prompt=login`)
|
|
171
|
+
3. **New account is added** to localStorage
|
|
172
|
+
4. **User can switch** between accounts from ProfileMenu
|
|
173
|
+
5. **API calls automatically use** the active account's token
|
|
174
|
+
|
|
175
|
+
**No manual code needed** - it just works!
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
235
179
|
## API Reference
|
|
236
180
|
|
|
237
|
-
### `TopBar`
|
|
181
|
+
### `TopBar` Component
|
|
238
182
|
|
|
239
|
-
Combined
|
|
183
|
+
Combined apps dropdown + profile menu (recommended).
|
|
240
184
|
|
|
241
185
|
#### Props
|
|
242
186
|
|
|
243
187
|
| Prop | Type | Required | Default | Description |
|
|
244
188
|
|------|------|----------|---------|-------------|
|
|
245
189
|
| `apiUrl` | `string` | Yes | - | Base URL for apps API |
|
|
190
|
+
| `accountManager` | `UseAccountManagerReturn` | Yes | - | Account manager from `useSpryAccountManager()` |
|
|
246
191
|
| `profileApiUrl` | `string` | No | Same as `apiUrl` | Base URL for profile API |
|
|
247
192
|
| `onSignOut` | `() => void` | No | - | Callback when user signs out |
|
|
248
|
-
| `getAuthToken` | `() => string \| null
|
|
193
|
+
| `getAuthToken` | `() => string \| null` | No | - | Function to get auth token |
|
|
249
194
|
| `profileHeaders` | `Record<string, string>` | No | - | Custom headers for profile API |
|
|
250
195
|
| `showAppsDropdown` | `boolean` | No | `true` | Show apps dropdown |
|
|
251
196
|
| `showProfileMenu` | `boolean` | No | `true` | Show profile menu |
|
|
252
|
-
| `appsRefetchInterval` | `number` | No | `300000` (5 min) | Apps refetch interval
|
|
253
|
-
| `appsCacheTime` | `number` | No | `300000` (5 min) | Apps cache duration
|
|
254
|
-
| `profileRefetchInterval` | `number` | No | `300000` (5 min) | Profile refetch interval
|
|
255
|
-
| `profileCacheTime` | `number` | No | `300000` (5 min) | Profile cache duration
|
|
197
|
+
| `appsRefetchInterval` | `number` | No | `300000` (5 min) | Apps refetch interval (ms) |
|
|
198
|
+
| `appsCacheTime` | `number` | No | `300000` (5 min) | Apps cache duration (ms) |
|
|
199
|
+
| `profileRefetchInterval` | `number` | No | `300000` (5 min) | Profile refetch interval (ms) |
|
|
200
|
+
| `profileCacheTime` | `number` | No | `300000` (5 min) | Profile cache duration (ms) |
|
|
256
201
|
|
|
257
|
-
|
|
202
|
+
---
|
|
258
203
|
|
|
259
|
-
|
|
204
|
+
### `SpryAuthProvider` Component
|
|
260
205
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
| Prop | Type | Required | Default | Description |
|
|
264
|
-
|------|------|----------|---------|-------------|
|
|
265
|
-
| `apiUrl` | `string` | Yes | - | Base URL for profile API |
|
|
266
|
-
| `onSignOut` | `() => void` | No | - | Callback when user signs out |
|
|
267
|
-
| `onManageAccount` | `() => void` | No | - | Callback for Manage your Account button. If not provided, it redirects to `https://sprylogin.com/my-account/` |
|
|
268
|
-
| `getAuthToken` | `() => string \| null \| Promise<string \| null>` | No | - | Function to get auth token |
|
|
269
|
-
| `headers` | `Record<string, string>` | No | - | Custom headers for API requests |
|
|
270
|
-
| `refetchInterval` | `number` | No | `300000` (5 min) | Auto-refetch interval in ms |
|
|
271
|
-
| `cacheTime` | `number` | No | `300000` (5 min) | Cache duration in ms |
|
|
272
|
-
|
|
273
|
-
### Manage Account behavior
|
|
274
|
-
|
|
275
|
-
The Manage your Account button is always shown. It uses this order:
|
|
276
|
-
|
|
277
|
-
1. `onManageAccount` callback (if provided)
|
|
278
|
-
2. `profile.manageAccountUrl` (if present)
|
|
279
|
-
3. Default: `https://sprylogin.com/my-account/`
|
|
280
|
-
|
|
281
|
-
### `AppsDropdownConnected`
|
|
282
|
-
|
|
283
|
-
The main component with automatic API integration.
|
|
206
|
+
Wraps your app with Keycloak/OIDC authentication.
|
|
284
207
|
|
|
285
208
|
#### Props
|
|
286
209
|
|
|
287
|
-
| Prop | Type | Required |
|
|
288
|
-
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
291
|
-
| `onClose` | `() => void` | Yes | - | Callback when dropdown closes |
|
|
292
|
-
| `buttonRef` | `React.RefObject<HTMLButtonElement>` | No | - | Ref for positioning |
|
|
293
|
-
| `refetchInterval` | `number` | No | `300000` (5 min) | Auto-refetch interval in ms |
|
|
294
|
-
| `cacheTime` | `number` | No | `300000` (5 min) | Cache duration in ms |
|
|
295
|
-
|
|
296
|
-
### `AppsDropdown`
|
|
210
|
+
| Prop | Type | Required | Description |
|
|
211
|
+
|------|------|----------|-------------|
|
|
212
|
+
| `config` | `UserManagerSettings` | Yes | OIDC configuration object |
|
|
213
|
+
| `children` | `ReactNode` | Yes | Your app components |
|
|
297
214
|
|
|
298
|
-
|
|
215
|
+
**Example config:**
|
|
299
216
|
|
|
300
|
-
|
|
217
|
+
```tsx
|
|
218
|
+
const oidcConfig = {
|
|
219
|
+
authority: 'https://auth.sprylogin.com/realms/sprylogin',
|
|
220
|
+
client_id: 'my-app-client',
|
|
221
|
+
redirect_uri: window.location.origin,
|
|
222
|
+
post_logout_redirect_uri: window.location.origin,
|
|
223
|
+
onSigninCallback: () => {
|
|
224
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
```
|
|
301
228
|
|
|
302
|
-
|
|
303
|
-
|------|------|----------|---------|-------------|
|
|
304
|
-
| `apps` | `App[]` | Yes | - | Array of apps to display |
|
|
305
|
-
| `open` | `boolean` | Yes | - | Whether the dropdown is open |
|
|
306
|
-
| `onClose` | `() => void` | Yes | - | Callback when dropdown closes |
|
|
307
|
-
| `buttonRef` | `React.RefObject<HTMLButtonElement>` | No | - | Ref for positioning |
|
|
308
|
-
| `isLoading` | `boolean` | No | `false` | Loading state |
|
|
309
|
-
| `error` | `string \| null` | No | `null` | Error message |
|
|
310
|
-
| `onRetry` | `() => void` | No | - | Retry callback |
|
|
229
|
+
---
|
|
311
230
|
|
|
312
|
-
### `
|
|
231
|
+
### `useSpryAccountManager()` Hook
|
|
313
232
|
|
|
314
|
-
|
|
233
|
+
Returns account manager for multi-account switching.
|
|
315
234
|
|
|
316
|
-
####
|
|
235
|
+
#### Returns
|
|
317
236
|
|
|
318
237
|
```typescript
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
238
|
+
{
|
|
239
|
+
accounts: StoredAccount[] // All logged-in accounts
|
|
240
|
+
activeAccount: StoredAccount // Currently active account
|
|
241
|
+
switchAccount: (id: string) => Promise<void>
|
|
242
|
+
addNewAccount: () => Promise<void>
|
|
243
|
+
removeAccount: (id: string) => Promise<void>
|
|
244
|
+
refreshAccountToken: (id: string) => Promise<void>
|
|
245
|
+
}
|
|
323
246
|
```
|
|
324
247
|
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### `useSpryAuth()` Hook
|
|
251
|
+
|
|
252
|
+
Returns the current authentication state (alias for `useAuth()` from react-oidc-context).
|
|
253
|
+
|
|
325
254
|
#### Returns
|
|
326
255
|
|
|
327
256
|
```typescript
|
|
328
257
|
{
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
258
|
+
user: User | null // Current user object
|
|
259
|
+
isAuthenticated: boolean // Is user logged in
|
|
260
|
+
isLoading: boolean // Is auth loading
|
|
261
|
+
signinRedirect: () => Promise<void>
|
|
262
|
+
signoutRedirect: () => Promise<void>
|
|
263
|
+
// ... other react-oidc-context methods
|
|
333
264
|
}
|
|
334
265
|
```
|
|
335
266
|
|
|
336
|
-
|
|
267
|
+
---
|
|
337
268
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
269
|
+
## Advanced: Handling Account Switches
|
|
270
|
+
|
|
271
|
+
If you're using React Query and want to refetch data when accounts switch:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import { useSpryAccountManager } from 'spry-apps-dropdown'
|
|
275
|
+
import { useQueryClient } from '@tanstack/react-query'
|
|
276
|
+
import { useEffect } from 'react'
|
|
277
|
+
|
|
278
|
+
function useMultiAccountManager() {
|
|
279
|
+
const queryClient = useQueryClient()
|
|
280
|
+
const accountManager = useSpryAccountManager()
|
|
281
|
+
|
|
282
|
+
// Listen for account switches and invalidate queries
|
|
283
|
+
useEffect(() => {
|
|
284
|
+
const handleAccountSwitch = () => {
|
|
285
|
+
queryClient.invalidateQueries() // Refetch all data
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
window.addEventListener('auth:account-switched', handleAccountSwitch)
|
|
289
|
+
return () => window.removeEventListener('auth:account-switched', handleAccountSwitch)
|
|
290
|
+
}, [queryClient])
|
|
291
|
+
|
|
292
|
+
return accountManager
|
|
349
293
|
}
|
|
350
294
|
```
|
|
351
295
|
|
|
296
|
+
---
|
|
297
|
+
|
|
352
298
|
## Backend API Requirements
|
|
353
299
|
|
|
354
|
-
|
|
300
|
+
Your backend should expose these endpoints:
|
|
355
301
|
|
|
356
302
|
### `GET /api/apps`
|
|
357
303
|
|
|
358
|
-
Returns
|
|
304
|
+
Returns list of apps for the dropdown.
|
|
305
|
+
|
|
359
306
|
```json
|
|
360
307
|
{
|
|
361
308
|
"apps": [
|
|
@@ -375,26 +322,17 @@ Returns:
|
|
|
375
322
|
}
|
|
376
323
|
```
|
|
377
324
|
|
|
378
|
-
### `GET /api/profile`
|
|
325
|
+
### `GET /api/profile`
|
|
379
326
|
|
|
380
|
-
Returns user profile information
|
|
327
|
+
Returns user profile information (requires Bearer token).
|
|
381
328
|
|
|
382
|
-
**Option 1: JWT Token (via Authorization header)**
|
|
383
329
|
```http
|
|
384
330
|
GET /api/profile
|
|
385
331
|
Authorization: Bearer <jwt-token>
|
|
386
332
|
```
|
|
387
333
|
|
|
388
|
-
**Option 2: Custom Headers**
|
|
389
|
-
```http
|
|
390
|
-
GET /api/profile
|
|
391
|
-
X-User-Email: user@example.com
|
|
392
|
-
X-User-FirstName: John
|
|
393
|
-
X-User-LastName: Doe
|
|
394
|
-
X-User-Avatar: https://example.com/avatar.jpg
|
|
395
|
-
```
|
|
396
|
-
|
|
397
334
|
**Response:**
|
|
335
|
+
|
|
398
336
|
```json
|
|
399
337
|
{
|
|
400
338
|
"email": "user@example.com",
|
|
@@ -405,52 +343,113 @@ X-User-Avatar: https://example.com/avatar.jpg
|
|
|
405
343
|
}
|
|
406
344
|
```
|
|
407
345
|
|
|
408
|
-
|
|
346
|
+
---
|
|
409
347
|
|
|
410
|
-
##
|
|
348
|
+
## Customization
|
|
411
349
|
|
|
412
|
-
|
|
350
|
+
### Custom Theme
|
|
413
351
|
|
|
414
352
|
```tsx
|
|
415
353
|
import { ThemeProvider, createTheme } from '@mui/material/styles'
|
|
416
354
|
|
|
417
355
|
const theme = createTheme({
|
|
418
|
-
|
|
356
|
+
palette: {
|
|
357
|
+
primary: {
|
|
358
|
+
main: '#1a73e8',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
419
361
|
})
|
|
420
362
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
<
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
)
|
|
427
|
-
}
|
|
363
|
+
<ThemeProvider theme={theme}>
|
|
364
|
+
<SpryAuthProvider config={oidcConfig}>
|
|
365
|
+
<App />
|
|
366
|
+
</SpryAuthProvider>
|
|
367
|
+
</ThemeProvider>
|
|
428
368
|
```
|
|
429
369
|
|
|
430
|
-
|
|
370
|
+
### Hide Apps Dropdown or Profile Menu
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
<TopBar
|
|
374
|
+
apiUrl="https://sprylogin.com/apps-api"
|
|
375
|
+
showAppsDropdown={false} // Hide apps dropdown
|
|
376
|
+
showProfileMenu={true} // Show only profile menu
|
|
377
|
+
accountManager={accountManager}
|
|
378
|
+
/>
|
|
379
|
+
```
|
|
431
380
|
|
|
432
|
-
|
|
381
|
+
---
|
|
433
382
|
|
|
434
|
-
##
|
|
383
|
+
## Troubleshooting
|
|
435
384
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
385
|
+
### "User not authenticated" error
|
|
386
|
+
|
|
387
|
+
Make sure you wrapped your app with `SpryAuthProvider`:
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
<SpryAuthProvider config={oidcConfig}>
|
|
391
|
+
<App />
|
|
392
|
+
</SpryAuthProvider>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Account switching doesn't work
|
|
396
|
+
|
|
397
|
+
Pass the `accountManager` prop to `TopBar`:
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
const accountManager = useSpryAccountManager()
|
|
401
|
+
|
|
402
|
+
<TopBar accountManager={accountManager} {...otherProps} />
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### API calls use wrong token after switching
|
|
406
|
+
|
|
407
|
+
Use `useSpryAuth()` to get the current token:
|
|
408
|
+
|
|
409
|
+
```tsx
|
|
410
|
+
const auth = useSpryAuth()
|
|
411
|
+
const token = auth.user?.access_token
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## TypeScript Support
|
|
417
|
+
|
|
418
|
+
Full TypeScript support included. Types are automatically available:
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
import type {
|
|
422
|
+
UseAccountManagerReturn,
|
|
423
|
+
StoredAccount,
|
|
424
|
+
OIDCUser,
|
|
425
|
+
TopBarProps
|
|
426
|
+
} from 'spry-apps-dropdown'
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
439
430
|
|
|
440
431
|
## Browser Support
|
|
441
432
|
|
|
442
433
|
- Modern browsers with ES2020 support
|
|
443
434
|
- Requires localStorage API
|
|
444
435
|
|
|
436
|
+
---
|
|
437
|
+
|
|
445
438
|
## License
|
|
446
439
|
|
|
447
440
|
MIT
|
|
448
441
|
|
|
449
|
-
|
|
442
|
+
---
|
|
450
443
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
## Related
|
|
444
|
+
## Related Documentation
|
|
454
445
|
|
|
446
|
+
- [INTEGRATION.md](./INTEGRATION.md) - Detailed multi-account integration guide
|
|
455
447
|
- [Spry Apps Server](https://github.com/your-org/spry-apps-server) - Backend API server
|
|
456
|
-
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Support
|
|
452
|
+
|
|
453
|
+
For issues or questions:
|
|
454
|
+
- Open an issue on GitHub
|
|
455
|
+
- Check INTEGRATION.md for detailed guides
|