@meshagent/meshagent-react-auth 0.36.3 → 0.37.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/CHANGELOG.md +19 -0
- package/README.md +406 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +2 -4
- package/dist/cjs/mauth-response.d.ts +9 -1
- package/dist/cjs/mauth-response.js +75 -35
- package/dist/cjs/reference-key.d.ts +1 -0
- package/dist/cjs/reference-key.js +18 -0
- package/dist/cjs/use-auth.d.ts +35 -0
- package/dist/cjs/use-auth.js +482 -0
- package/dist/cjs/use-ensure-login.d.ts +13 -0
- package/dist/cjs/use-ensure-login.js +29 -0
- package/dist/cjs/use-login-scope.d.ts +4 -14
- package/dist/cjs/use-login-scope.js +5 -222
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/mauth-response.d.ts +9 -1
- package/dist/esm/mauth-response.js +75 -35
- package/dist/esm/reference-key.d.ts +1 -0
- package/dist/esm/reference-key.js +15 -0
- package/dist/esm/use-auth.d.ts +35 -0
- package/dist/esm/use-auth.js +478 -0
- package/dist/esm/use-ensure-login.d.ts +13 -0
- package/dist/esm/use-ensure-login.js +26 -0
- package/dist/esm/use-login-scope.d.ts +4 -14
- package/dist/esm/use-login-scope.js +5 -221
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [0.37.2]
|
|
2
|
+
- Stability
|
|
3
|
+
|
|
4
|
+
## [0.37.1]
|
|
5
|
+
- Added `useAuth` to the React auth package to handle OAuth redirect/callback, token refresh, and profile loading without React Query, plus a `useEnsureLogin` compatibility wrapper.
|
|
6
|
+
- React auth `useMAuthResponse` now deduplicates token exchanges per callback parameters to avoid repeated exchanges on re-renders.
|
|
7
|
+
- Breaking: React auth no longer exports React Query `QueryClient` or `QueryClientProvider`.
|
|
8
|
+
- Breaking: `useLoginScope` now delegates to `useAuth` and defaults the OAuth scope to `email`.
|
|
9
|
+
- Breaking: the `staticAuthorization` helper was removed from the React room connection utilities.
|
|
10
|
+
- React dev terminal hooks now return `RefObject`-typed `containerRef` values for stronger typing.
|
|
11
|
+
- Meshagent TS client now normalizes binary request bodies for fetch and includes TypeScript declarations for runtime entrypoint functions.
|
|
12
|
+
|
|
13
|
+
## [0.37.0]
|
|
14
|
+
- Breaking: Database client now supports `json`, `uuid`, `list`, and `struct` types with typed wrappers (DatabaseJson/DatabaseStruct/DatabaseExpression/DatabaseDate/DatabaseUuid); list/struct values must be wrapped and update now takes `values` only.
|
|
15
|
+
- Breaking: Containers build now streams build contexts (start/data chunks) with `mountPath`/`chunks` and removes `start_build`.
|
|
16
|
+
- Breaking: Toolkit/hosting refactor replaces RemoteToolkit with startHostedToolkit/HostedToolkit, removes ToolkitConfiguration and `supports_context`, and updates React/Tailwind helpers to start hosted toolkits.
|
|
17
|
+
- Participant tokens now include LLM grants and richer grant serialization (including allowed toolkits and extra payload preservation), and schema helpers add `json`/`uuid` data types.
|
|
18
|
+
- The JS SDK now exports its version constant for client-side visibility.
|
|
19
|
+
|
|
1
20
|
## [0.36.3]
|
|
2
21
|
- Storage client now supports move operations and emits file moved events.
|
|
3
22
|
- Secrets client now supports existence checks.
|
package/README.md
CHANGED
|
@@ -1,3 +1,408 @@
|
|
|
1
1
|
# Meshagent React Auth
|
|
2
2
|
|
|
3
|
-
React helpers for
|
|
3
|
+
React helpers for browser OAuth login against Meshagent.
|
|
4
|
+
|
|
5
|
+
This package sits on top of `@meshagent/meshagent-ts-auth` and gives you React-friendly hooks for:
|
|
6
|
+
|
|
7
|
+
- starting a PKCE OAuth redirect
|
|
8
|
+
- exchanging the callback `code` for tokens
|
|
9
|
+
- refreshing access tokens before protected requests
|
|
10
|
+
- loading the current user profile into auth state
|
|
11
|
+
- subscribing to auth state from React components
|
|
12
|
+
|
|
13
|
+
It is designed for browser React apps and does not require React Query or a provider wrapper.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @meshagent/meshagent-react-auth
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If your app also imports lower-level auth primitives or the core Meshagent client directly, add those as direct dependencies too:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @meshagent/meshagent-ts-auth @meshagent/meshagent
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Before You Get Started
|
|
28
|
+
|
|
29
|
+
Create an OAuth client in Meshagent Studio and register your callback URL.
|
|
30
|
+
|
|
31
|
+
1. Sign in to https://studio.meshagent.com.
|
|
32
|
+
2. Select your project
|
|
33
|
+
3. Open "OAuth Clients" at the bottom of the left menu
|
|
34
|
+
4. Click "New OAuth Client" button at the top
|
|
35
|
+
5. Add
|
|
36
|
+
- name for the client id (e.g. "My App")
|
|
37
|
+
- enter "email" for the scope
|
|
38
|
+
- add redirect URIs (e.g. "http://localhost:3000/")
|
|
39
|
+
6. Upon successful creation, you will see the client id and client secret. Click "Copy" button to copy the client id and client secret to clipboard. You will need them later when you run the app.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
You will need these values throughout the examples:
|
|
43
|
+
|
|
44
|
+
- `oauthClientId`: the OAuth client ID from Meshagent Studio
|
|
45
|
+
- `callbackUrl`: the URL Meshagent redirects back to after login - this must match the redirect URI you registered in Studio
|
|
46
|
+
|
|
47
|
+
Optional
|
|
48
|
+
- `serverUrl`: your Meshagent API base URL, uses `https://api.meshagent.com` as default
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## What This Package Exports
|
|
52
|
+
|
|
53
|
+
- `useAuth`: main hook for login, callback handling, session refresh, and current-user loading
|
|
54
|
+
- `useLoginScope`: compatibility alias for the older signed-in hook API
|
|
55
|
+
- `useEnsureLogin`: compatibility alias for the older same-page login API
|
|
56
|
+
- `useMAuthResponse`: callback hook that exchanges the OAuth authorization code
|
|
57
|
+
- `useMeshagentAuth`: React subscription hook for auth state
|
|
58
|
+
- `buildOAuthAuthorizeUrl`: manual helper for custom login launches
|
|
59
|
+
|
|
60
|
+
## Typical Flow
|
|
61
|
+
|
|
62
|
+
1. Render your app normally. No auth provider wrapper is required.
|
|
63
|
+
2. For the simplest same-page flow, run `useAuth(...)` and read auth state with `useMeshagentAuth()`.
|
|
64
|
+
3. If you prefer a dedicated callback route, render `useMAuthResponse(...)` on that route and protect the rest of the app with `useAuth(...)`.
|
|
65
|
+
4. Read live auth state anywhere with `useMeshagentAuth()`.
|
|
66
|
+
|
|
67
|
+
`useAuth(...)` defaults `autoSignIn` to `true`, so it will immediately launch the OAuth redirect when there is no session yet. Set `autoSignIn: false` when you want to show your own "Sign in" button.
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### Simplest Flow: `useAuth(...)`
|
|
72
|
+
|
|
73
|
+
Use `useAuth(...)` when you want the same component to handle both the initial login redirect and the callback return.
|
|
74
|
+
|
|
75
|
+
If you omit `callbackUrl`, it uses the current page as the callback URL and strips the OAuth query params after a successful return.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import {
|
|
79
|
+
useAuth,
|
|
80
|
+
useMeshagentAuth,
|
|
81
|
+
} from "@meshagent/meshagent-react-auth";
|
|
82
|
+
|
|
83
|
+
const oauthClientId = "YOUR_OAUTH_CLIENT_ID";
|
|
84
|
+
|
|
85
|
+
export function App(): JSX.Element {
|
|
86
|
+
const { failed, refreshing, refresh } = useAuth({
|
|
87
|
+
oauthClientId,
|
|
88
|
+
});
|
|
89
|
+
const auth = useMeshagentAuth();
|
|
90
|
+
|
|
91
|
+
if (refreshing) {
|
|
92
|
+
return <p>Signing you in...</p>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (failed) {
|
|
96
|
+
return (
|
|
97
|
+
<div>
|
|
98
|
+
<p>Authentication failed: {String(failed)}</p>
|
|
99
|
+
<button onClick={refresh}>Retry</button>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return <pre>{JSON.stringify(auth, null, 2)}</pre>;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`useAuth(...)` uses the default shared `meshagentAuth` store by default, but it also accepts custom `auth` and `storage` instances when you need them. `useEnsureLogin(...)` remains available as a compatibility alias for this same-page flow.
|
|
109
|
+
|
|
110
|
+
### 1. Handle The OAuth Callback
|
|
111
|
+
|
|
112
|
+
Use `useMAuthResponse(...)` on the route that matches your registered callback URL.
|
|
113
|
+
|
|
114
|
+
If you do not pass `authorizationCode`, the hook reads `?code=...` from `window.location`.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { useEffect } from "react";
|
|
118
|
+
|
|
119
|
+
import { useMAuthResponse } from "@meshagent/meshagent-react-auth";
|
|
120
|
+
|
|
121
|
+
// callbackUrl must match redirect URI registered in Studio, e.g. "http://localhost:3000/auth/callback"
|
|
122
|
+
const callbackUrl = new URL("/auth/callback", window.location.origin);
|
|
123
|
+
const oauthClientId = "YOUR_OAUTH_CLIENT_ID";
|
|
124
|
+
|
|
125
|
+
export function AuthCallbackPage(): JSX.Element {
|
|
126
|
+
const { status, error } = useMAuthResponse({
|
|
127
|
+
callbackUrl,
|
|
128
|
+
oauthClientId,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (status === "success") {
|
|
133
|
+
window.location.replace("/");
|
|
134
|
+
}
|
|
135
|
+
}, [status]);
|
|
136
|
+
|
|
137
|
+
if (status === "idle") {
|
|
138
|
+
return <p>Missing authorization code.</p>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (status === "loading") {
|
|
142
|
+
return <p>Signing you in...</p>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (status === "error") {
|
|
146
|
+
return <p>Authentication failed: {error}</p>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return <p>Login complete. Redirecting...</p>;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
After a successful exchange, the hook stores the access token, refresh token, expiration, and current user in the underlying auth store.
|
|
154
|
+
|
|
155
|
+
### 2. Protect The Signed-In Part Of Your App
|
|
156
|
+
|
|
157
|
+
Use `useAuth(...)` anywhere you want to ensure that:
|
|
158
|
+
|
|
159
|
+
- the user has a session
|
|
160
|
+
- the access token is still valid enough
|
|
161
|
+
- the current user profile has been loaded into auth state
|
|
162
|
+
|
|
163
|
+
The example below disables auto-redirect so the UI can show manual sign-in buttons.
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import {
|
|
167
|
+
useAuth,
|
|
168
|
+
useMeshagentAuth,
|
|
169
|
+
} from "@meshagent/meshagent-react-auth";
|
|
170
|
+
import { meshagentAuth } from "@meshagent/meshagent-ts-auth";
|
|
171
|
+
|
|
172
|
+
const callbackUrl = new URL("/auth/callback", window.location.origin);
|
|
173
|
+
const oauthClientId = "YOUR_OAUTH_CLIENT_ID";
|
|
174
|
+
|
|
175
|
+
export function ProtectedApp(): JSX.Element {
|
|
176
|
+
const {
|
|
177
|
+
failed,
|
|
178
|
+
isSigningIn,
|
|
179
|
+
refreshing,
|
|
180
|
+
signIn,
|
|
181
|
+
refresh,
|
|
182
|
+
user,
|
|
183
|
+
} = useAuth({
|
|
184
|
+
callbackUrl,
|
|
185
|
+
oauthClientId,
|
|
186
|
+
scope: "email",
|
|
187
|
+
autoSignIn: false,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (refreshing) {
|
|
191
|
+
return <p>Checking session...</p>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!user) {
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
{failed ? <p>Authentication failed: {String(failed)}</p> : null}
|
|
198
|
+
|
|
199
|
+
<button onClick={() => void signIn()} disabled={isSigningIn}>
|
|
200
|
+
Sign in
|
|
201
|
+
</button>
|
|
202
|
+
|
|
203
|
+
<button onClick={() => void signIn("google")} disabled={isSigningIn}>
|
|
204
|
+
Continue with Google
|
|
205
|
+
</button>
|
|
206
|
+
|
|
207
|
+
<button onClick={refresh} disabled={isSigningIn}>
|
|
208
|
+
Retry
|
|
209
|
+
</button>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return <AuthenticatedHome />;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function AuthenticatedHome(): JSX.Element {
|
|
218
|
+
const auth = useMeshagentAuth();
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div>
|
|
222
|
+
<p>Signed in.</p>
|
|
223
|
+
<pre>{JSON.stringify(auth, null, 2)}</pre>
|
|
224
|
+
<button onClick={() => meshagentAuth.signOut()}>Sign out</button>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
If you want automatic redirect instead of a sign-in screen, omit `autoSignIn`:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
const auth = useAuth({ callbackUrl, oauthClientId });
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
When there is no session, that hook will immediately start the OAuth redirect flow.
|
|
237
|
+
|
|
238
|
+
### 3. Read Auth State Anywhere
|
|
239
|
+
|
|
240
|
+
`useMeshagentAuth()` is the smallest hook in the package. It subscribes to the underlying auth store and returns the current snapshot:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { useMeshagentAuth } from "@meshagent/meshagent-react-auth";
|
|
244
|
+
|
|
245
|
+
export function SessionDebugPanel(): JSX.Element {
|
|
246
|
+
const auth = useMeshagentAuth();
|
|
247
|
+
|
|
248
|
+
return <pre>{JSON.stringify(auth, null, 2)}</pre>;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The returned snapshot has this shape:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
{
|
|
256
|
+
accessToken: string | null;
|
|
257
|
+
refreshToken: string | null;
|
|
258
|
+
expiration: Date | null;
|
|
259
|
+
user: Record<string, unknown> | null;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`user` is intentionally application-specific. Model it in your app instead of assuming fixed fields in shared code.
|
|
264
|
+
|
|
265
|
+
## Example: Manual Authorize URL
|
|
266
|
+
|
|
267
|
+
Use `buildOAuthAuthorizeUrl(...)` when you want manual control over when and how the browser is redirected.
|
|
268
|
+
|
|
269
|
+
This helper also saves the PKCE verifier so that `useMAuthResponse(...)` can complete the callback exchange later.
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
import { buildOAuthAuthorizeUrl } from "@meshagent/meshagent-react-auth";
|
|
273
|
+
|
|
274
|
+
const serverUrl = "https://api.meshagent.com";
|
|
275
|
+
const callbackUrl = new URL("/auth/callback", window.location.origin);
|
|
276
|
+
const oauthClientId = "YOUR_OAUTH_CLIENT_ID";
|
|
277
|
+
|
|
278
|
+
export async function signInWithGoogle(): Promise<void> {
|
|
279
|
+
const authorizeUrl = await buildOAuthAuthorizeUrl({
|
|
280
|
+
serverUrl,
|
|
281
|
+
callbackUrl,
|
|
282
|
+
oauthClientId,
|
|
283
|
+
scope: "email",
|
|
284
|
+
provider: "google",
|
|
285
|
+
extraQueryParams: {
|
|
286
|
+
invitation: "abc123",
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
window.location.assign(authorizeUrl.toString());
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
If you pass custom `auth` or `storage`, use the same instances in both the login-launch side and the callback side.
|
|
295
|
+
Passing `storage` alone changes PKCE verifier storage. Session persistence follows the `auth` instance you use.
|
|
296
|
+
|
|
297
|
+
## API Reference
|
|
298
|
+
|
|
299
|
+
### `useAuth(options)`
|
|
300
|
+
|
|
301
|
+
Primary auth hook for browser login flows.
|
|
302
|
+
|
|
303
|
+
What it does:
|
|
304
|
+
|
|
305
|
+
- launches the browser OAuth redirect when there is no session yet
|
|
306
|
+
- exchanges the callback `code` when the current URL matches the callback URL
|
|
307
|
+
- loads the current user into auth state
|
|
308
|
+
- refreshes the access token when needed before protected requests
|
|
309
|
+
- signs out automatically if token refresh fails
|
|
310
|
+
- signs out automatically if profile loading returns `401` or `403`
|
|
311
|
+
- strips OAuth query params from the current URL after a successful callback exchange
|
|
312
|
+
- dedupes duplicate effect runs for the same login attempt
|
|
313
|
+
|
|
314
|
+
Important options:
|
|
315
|
+
|
|
316
|
+
- `oauthClientId`: required OAuth client ID
|
|
317
|
+
- `serverUrl`: optional Meshagent base URL, defaults to `https://api.meshagent.com`
|
|
318
|
+
- `callbackUrl`: optional callback URL, defaults to the current page
|
|
319
|
+
- `scope`: optional OAuth scope, defaults to `"profile"`
|
|
320
|
+
- `provider`: optional provider slug to pass through to `/oauth/authorize`
|
|
321
|
+
- `extraQueryParams`: optional extra query params for `/oauth/authorize`
|
|
322
|
+
- `storage`: optional custom storage for PKCE verifier caching
|
|
323
|
+
- `auth`: optional custom `MeshagentAuth` instance
|
|
324
|
+
- `autoSignIn`: optional, defaults to `true`
|
|
325
|
+
|
|
326
|
+
Returned fields:
|
|
327
|
+
|
|
328
|
+
| Field | Meaning |
|
|
329
|
+
| --- | --- |
|
|
330
|
+
| `failed` | Most recent error, or `null` |
|
|
331
|
+
| `isCancelled` | Currently always `false` in this browser redirect implementation |
|
|
332
|
+
| `isSigningIn` | `true` while a sign-in launch is in progress |
|
|
333
|
+
| `refreshing` | `true` while callback exchange, session validation, or profile loading is running |
|
|
334
|
+
| `user` | Current user from auth state, or `null` |
|
|
335
|
+
| `signIn(provider?)` | Starts the OAuth redirect, optionally forcing a provider |
|
|
336
|
+
| `refresh()` | Re-runs the auth flow |
|
|
337
|
+
| `isLoginLaunched` | Useful for deciding when to show login or retry UI |
|
|
338
|
+
|
|
339
|
+
`useAuth(...)` replaces the overlap between `useEnsureLogin(...)` and `useLoginScope(...)`. Both legacy names still exist as wrappers for compatibility.
|
|
340
|
+
|
|
341
|
+
### Legacy Aliases
|
|
342
|
+
|
|
343
|
+
`useEnsureLogin(options)` is a compatibility wrapper over `useAuth(...)` for the old same-page login API. It still returns:
|
|
344
|
+
|
|
345
|
+
| Field | Meaning |
|
|
346
|
+
| --- | --- |
|
|
347
|
+
| `status` | `"loading"`, `"success"`, or `"error"` |
|
|
348
|
+
| `error` | Error message string, or `null` |
|
|
349
|
+
| `refresh()` | Re-runs the auth flow |
|
|
350
|
+
|
|
351
|
+
`useLoginScope(options)` is a compatibility wrapper over `useAuth(...)` for the older signed-in hook API. It returns the same fields as `useAuth(...)`, while keeping `callbackUrl` required and preserving its previous default `scope` behavior.
|
|
352
|
+
|
|
353
|
+
### `useMAuthResponse(props)`
|
|
354
|
+
|
|
355
|
+
Hook for your OAuth callback route.
|
|
356
|
+
|
|
357
|
+
What it does:
|
|
358
|
+
|
|
359
|
+
- reads the authorization code from `authorizationCode` or the current URL
|
|
360
|
+
- exchanges the code at `/oauth/token`
|
|
361
|
+
- stores access token, refresh token, and expiration in auth state
|
|
362
|
+
- loads the current user profile
|
|
363
|
+
- stores that user in auth state
|
|
364
|
+
|
|
365
|
+
It does not navigate away after success. Redirect to the rest of your app yourself.
|
|
366
|
+
If you used custom storage for the PKCE verifier during login launch, pass that same `storage` here.
|
|
367
|
+
|
|
368
|
+
Return value:
|
|
369
|
+
|
|
370
|
+
| `status` | Meaning |
|
|
371
|
+
| --- | --- |
|
|
372
|
+
| `"idle"` | No authorization code was found |
|
|
373
|
+
| `"loading"` | Token exchange is in progress |
|
|
374
|
+
| `"success"` | Exchange and profile load completed |
|
|
375
|
+
| `"error"` | Exchange failed |
|
|
376
|
+
|
|
377
|
+
The returned `error` is the message string from the failed query, or `null`.
|
|
378
|
+
|
|
379
|
+
### `useMeshagentAuth(auth?)`
|
|
380
|
+
|
|
381
|
+
Subscribes to a `MeshagentAuth` store with `useSyncExternalStore` and returns the current snapshot.
|
|
382
|
+
|
|
383
|
+
Use this anywhere you want React components to update when tokens, expiration, or user state changes.
|
|
384
|
+
|
|
385
|
+
### `buildOAuthAuthorizeUrl(params)`
|
|
386
|
+
|
|
387
|
+
Builds the `/oauth/authorize` URL and stores a PKCE verifier before redirect.
|
|
388
|
+
|
|
389
|
+
Supported parameters:
|
|
390
|
+
|
|
391
|
+
- `serverUrl`
|
|
392
|
+
- `callbackUrl`
|
|
393
|
+
- `oauthClientId`
|
|
394
|
+
- `scope`
|
|
395
|
+
- `provider`
|
|
396
|
+
- `extraQueryParams`
|
|
397
|
+
- `storage`
|
|
398
|
+
|
|
399
|
+
Use this helper when the default `signIn()` behavior is too opinionated and you need manual redirect control.
|
|
400
|
+
`storage` here controls where the PKCE verifier is cached before the redirect.
|
|
401
|
+
|
|
402
|
+
## Notes
|
|
403
|
+
|
|
404
|
+
- This package is for browser redirect-based OAuth flows. `useAuth(...)` and its legacy aliases will throw if they try to launch login outside a browser environment.
|
|
405
|
+
- No provider wrapper is required. `useAuth(...)`, `useEnsureLogin(...)`, `useLoginScope(...)`, and `useMAuthResponse(...)` manage their own async state.
|
|
406
|
+
- `useMAuthResponse(...)` does not clear the `code` query parameter or navigate after success. Most apps should redirect away from the callback page after `status === "success"`.
|
|
407
|
+
- Sign-out functionality lives in `@meshagent/meshagent-ts-auth`, for example `meshagentAuth.signOut()`.
|
|
408
|
+
- If you need the low-level primitives directly, see `@meshagent/meshagent-ts-auth`. This package is the React layer on top of it.
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -14,10 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.QueryClientProvider = exports.QueryClient = void 0;
|
|
18
|
-
var react_query_1 = require("@tanstack/react-query");
|
|
19
|
-
Object.defineProperty(exports, "QueryClient", { enumerable: true, get: function () { return react_query_1.QueryClient; } });
|
|
20
|
-
Object.defineProperty(exports, "QueryClientProvider", { enumerable: true, get: function () { return react_query_1.QueryClientProvider; } });
|
|
21
17
|
__exportStar(require("./mauth-response"), exports);
|
|
18
|
+
__exportStar(require("./use-auth"), exports);
|
|
19
|
+
__exportStar(require("./use-ensure-login"), exports);
|
|
22
20
|
__exportStar(require("./use-login-scope"), exports);
|
|
23
21
|
__exportStar(require("./use-meshagent-auth"), exports);
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StorageLike, MeshagentAuth } from "@meshagent/meshagent-ts-auth";
|
|
2
2
|
declare enum UseMAuthResponseStatus {
|
|
3
3
|
Idle = "idle",
|
|
4
4
|
Loading = "loading",
|
|
5
5
|
Success = "success",
|
|
6
6
|
Error = "error"
|
|
7
7
|
}
|
|
8
|
+
export interface MAuthResponseProps {
|
|
9
|
+
serverUrl?: string | URL;
|
|
10
|
+
callbackUrl: string | URL;
|
|
11
|
+
oauthClientId: string;
|
|
12
|
+
authorizationCode?: string;
|
|
13
|
+
storage?: StorageLike;
|
|
14
|
+
auth?: MeshagentAuth;
|
|
15
|
+
}
|
|
8
16
|
export interface MAuthResponse {
|
|
9
17
|
status: UseMAuthResponseStatus;
|
|
10
18
|
error: string | null;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useMAuthResponse = useMAuthResponse;
|
|
4
|
-
const
|
|
4
|
+
const react_1 = require("react");
|
|
5
5
|
const meshagent_ts_auth_1 = require("@meshagent/meshagent-ts-auth");
|
|
6
|
+
const reference_key_1 = require("./reference-key");
|
|
6
7
|
function toUrl(value) {
|
|
7
8
|
return value instanceof URL ? value : new URL(value);
|
|
8
9
|
}
|
|
10
|
+
const inFlightTokenExchanges = new Map();
|
|
9
11
|
var UseMAuthResponseStatus;
|
|
10
12
|
(function (UseMAuthResponseStatus) {
|
|
11
13
|
UseMAuthResponseStatus["Idle"] = "idle";
|
|
@@ -13,50 +15,88 @@ var UseMAuthResponseStatus;
|
|
|
13
15
|
UseMAuthResponseStatus["Success"] = "success";
|
|
14
16
|
UseMAuthResponseStatus["Error"] = "error";
|
|
15
17
|
})(UseMAuthResponseStatus || (UseMAuthResponseStatus = {}));
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
function buildTokenExchangeKey(params) {
|
|
19
|
+
return JSON.stringify([
|
|
20
|
+
params.serverUrl,
|
|
21
|
+
params.callbackUrl,
|
|
22
|
+
params.oauthClientId,
|
|
23
|
+
params.code,
|
|
24
|
+
params.storageKey,
|
|
25
|
+
params.authKey,
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
function toErrorMessage(error) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
return error.message;
|
|
25
31
|
}
|
|
26
|
-
return
|
|
32
|
+
return String(error);
|
|
27
33
|
}
|
|
28
34
|
;
|
|
29
35
|
function useMAuthResponse({ serverUrl, callbackUrl, oauthClientId, authorizationCode, storage, auth, }) {
|
|
36
|
+
const serverUrlKey = toUrl(serverUrl ?? "http://api.meshcentral.com").toString();
|
|
37
|
+
const callbackUrlKey = toUrl(callbackUrl).toString();
|
|
30
38
|
const code = authorizationCode ?? (0, meshagent_ts_auth_1.getAuthorizationCode)() ?? undefined;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
const [status, setStatus] = (0, react_1.useState)(code === undefined ? UseMAuthResponseStatus.Idle : UseMAuthResponseStatus.Loading);
|
|
40
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
41
|
+
const tokenExchangeKey = code === undefined
|
|
42
|
+
? null
|
|
43
|
+
: buildTokenExchangeKey({
|
|
44
|
+
serverUrl: serverUrlKey,
|
|
45
|
+
callbackUrl: callbackUrlKey,
|
|
36
46
|
oauthClientId,
|
|
37
47
|
code,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
storageKey: (0, reference_key_1.getReferenceKey)(storage, "default-storage"),
|
|
49
|
+
authKey: (0, reference_key_1.getReferenceKey)(auth, "default-auth"),
|
|
50
|
+
});
|
|
51
|
+
(0, react_1.useEffect)(() => {
|
|
52
|
+
if (code === undefined || tokenExchangeKey === null) {
|
|
53
|
+
setStatus(UseMAuthResponseStatus.Idle);
|
|
54
|
+
setError(null);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let cancelled = false;
|
|
58
|
+
setStatus(UseMAuthResponseStatus.Loading);
|
|
59
|
+
setError(null);
|
|
60
|
+
let tokenExchange = inFlightTokenExchanges.get(tokenExchangeKey);
|
|
61
|
+
if (!tokenExchange) {
|
|
62
|
+
tokenExchange = (0, meshagent_ts_auth_1.exchangeToken)({
|
|
63
|
+
serverUrl: serverUrlKey,
|
|
49
64
|
callbackUrl,
|
|
50
65
|
oauthClientId,
|
|
51
66
|
authorizationCode: code,
|
|
52
67
|
storage,
|
|
53
68
|
auth,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
}).finally(() => {
|
|
70
|
+
inFlightTokenExchanges.delete(tokenExchangeKey);
|
|
71
|
+
});
|
|
72
|
+
inFlightTokenExchanges.set(tokenExchangeKey, tokenExchange);
|
|
73
|
+
}
|
|
74
|
+
tokenExchange
|
|
75
|
+
.then(() => {
|
|
76
|
+
if (cancelled) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
setStatus(UseMAuthResponseStatus.Success);
|
|
80
|
+
setError(null);
|
|
81
|
+
})
|
|
82
|
+
.catch((exchangeError) => {
|
|
83
|
+
if (cancelled) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setStatus(UseMAuthResponseStatus.Error);
|
|
87
|
+
setError(toErrorMessage(exchangeError));
|
|
88
|
+
});
|
|
89
|
+
return () => {
|
|
90
|
+
cancelled = true;
|
|
91
|
+
};
|
|
92
|
+
}, [
|
|
93
|
+
auth,
|
|
94
|
+
callbackUrl,
|
|
95
|
+
code,
|
|
96
|
+
oauthClientId,
|
|
97
|
+
serverUrl,
|
|
98
|
+
storage,
|
|
99
|
+
tokenExchangeKey,
|
|
100
|
+
]);
|
|
101
|
+
return { status, error };
|
|
62
102
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getReferenceKey(value: object | null | undefined, fallback: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getReferenceKey = getReferenceKey;
|
|
4
|
+
const objectIds = new WeakMap();
|
|
5
|
+
let nextObjectId = 1;
|
|
6
|
+
function getReferenceKey(value, fallback) {
|
|
7
|
+
if (!value) {
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
const existing = objectIds.get(value);
|
|
11
|
+
if (existing !== undefined) {
|
|
12
|
+
return `${fallback}:${existing}`;
|
|
13
|
+
}
|
|
14
|
+
const id = nextObjectId;
|
|
15
|
+
nextObjectId += 1;
|
|
16
|
+
objectIds.set(value, id);
|
|
17
|
+
return `${fallback}:${id}`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { MeshagentAuth } from "@meshagent/meshagent-ts-auth";
|
|
2
|
+
import type { StorageLike, MeshagentUser } from "@meshagent/meshagent-ts-auth";
|
|
3
|
+
export declare function buildOAuthAuthorizeUrl(params: {
|
|
4
|
+
serverUrl: string | URL;
|
|
5
|
+
callbackUrl: string | URL;
|
|
6
|
+
oauthClientId: string;
|
|
7
|
+
scope?: string;
|
|
8
|
+
provider?: string | null;
|
|
9
|
+
extraQueryParams?: Record<string, string>;
|
|
10
|
+
storage?: StorageLike;
|
|
11
|
+
}): Promise<URL>;
|
|
12
|
+
export interface AuthRenderProps {
|
|
13
|
+
failed: unknown;
|
|
14
|
+
isCancelled: boolean;
|
|
15
|
+
isSigningIn: boolean;
|
|
16
|
+
refreshing: boolean;
|
|
17
|
+
user: MeshagentUser | null;
|
|
18
|
+
signIn: (provider?: string | null) => Promise<void>;
|
|
19
|
+
refresh: () => void;
|
|
20
|
+
}
|
|
21
|
+
export interface UseAuthProps {
|
|
22
|
+
serverUrl?: string | URL;
|
|
23
|
+
callbackUrl?: string | URL;
|
|
24
|
+
oauthClientId: string;
|
|
25
|
+
scope?: string;
|
|
26
|
+
provider?: string;
|
|
27
|
+
extraQueryParams?: Record<string, string>;
|
|
28
|
+
storage?: StorageLike;
|
|
29
|
+
auth?: MeshagentAuth;
|
|
30
|
+
autoSignIn?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface UseAuthResult extends AuthRenderProps {
|
|
33
|
+
isLoginLaunched: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function useAuth(props: UseAuthProps): UseAuthResult;
|