@object-ui/auth 3.3.0 → 3.3.1
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 +107 -0
- package/README.md +21 -1
- package/dist/AuthContext.d.ts +58 -1
- package/dist/AuthContext.d.ts.map +1 -1
- package/dist/AuthProvider.d.ts.map +1 -1
- package/dist/AuthProvider.js +153 -1
- package/dist/LoginForm.d.ts.map +1 -1
- package/dist/LoginForm.js +2 -1
- package/dist/RegisterForm.d.ts.map +1 -1
- package/dist/RegisterForm.js +2 -1
- package/dist/SocialSignInButtons.d.ts +26 -0
- package/dist/SocialSignInButtons.d.ts.map +1 -0
- package/dist/SocialSignInButtons.js +97 -0
- package/dist/createAuthClient.d.ts +16 -4
- package/dist/createAuthClient.d.ts.map +1 -1
- package/dist/createAuthClient.js +256 -5
- package/dist/createAuthenticatedFetch.d.ts +15 -11
- package/dist/createAuthenticatedFetch.d.ts.map +1 -1
- package/dist/createAuthenticatedFetch.js +52 -11
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/useAuth.d.ts.map +1 -1
- package/dist/useAuth.js +21 -0
- package/package.json +32 -8
|
@@ -6,13 +6,25 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
import type { AuthClient, AuthClientConfig } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Simple token storage backed by localStorage.
|
|
11
|
+
* Falls back to in-memory storage when localStorage is unavailable (SSR, tests).
|
|
12
|
+
*/
|
|
13
|
+
export declare const TokenStorage: {
|
|
14
|
+
_memoryToken: string | null;
|
|
15
|
+
get(): string | null;
|
|
16
|
+
set(token: string): void;
|
|
17
|
+
clear(): void;
|
|
18
|
+
};
|
|
9
19
|
/**
|
|
10
20
|
* Create an auth client instance backed by the official better-auth client.
|
|
11
21
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
22
|
+
* Uses Bearer token authentication: tokens are stored in localStorage and
|
|
23
|
+
* sent via `Authorization: Bearer <token>` header on every request. This
|
|
24
|
+
* works across origins (no cookie dependency) and is compatible with mobile
|
|
25
|
+
* clients.
|
|
26
|
+
*
|
|
27
|
+
* Requires the server to have the better-auth `bearer()` plugin enabled.
|
|
16
28
|
*
|
|
17
29
|
* @example
|
|
18
30
|
* ```ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAuthClient.d.ts","sourceRoot":"","sources":["../src/createAuthClient.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"createAuthClient.d.ts","sourceRoot":"","sources":["../src/createAuthClient.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,UAAU,EAAE,gBAAgB,EAE7B,MAAM,SAAS,CAAC;AAIjB;;;GAGG;AACH,eAAO,MAAM,YAAY;kBACD,MAAM,GAAG,IAAI;WAE5B,MAAM,GAAG,IAAI;eAST,MAAM,GAAG,IAAI;aASf,IAAI;CAQd,CAAC;AA6DF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CA0RrE"}
|
package/dist/createAuthClient.js
CHANGED
|
@@ -6,6 +6,42 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
import { createAuthClient as createBetterAuthClient } from 'better-auth/client';
|
|
9
|
+
import { organizationClient } from 'better-auth/client/plugins';
|
|
10
|
+
const TOKEN_STORAGE_KEY = 'auth-session-token';
|
|
11
|
+
/**
|
|
12
|
+
* Simple token storage backed by localStorage.
|
|
13
|
+
* Falls back to in-memory storage when localStorage is unavailable (SSR, tests).
|
|
14
|
+
*/
|
|
15
|
+
export const TokenStorage = {
|
|
16
|
+
_memoryToken: null,
|
|
17
|
+
get() {
|
|
18
|
+
try {
|
|
19
|
+
if (typeof localStorage !== 'undefined') {
|
|
20
|
+
return localStorage.getItem(TOKEN_STORAGE_KEY);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch { /* SSR / test */ }
|
|
24
|
+
return this._memoryToken;
|
|
25
|
+
},
|
|
26
|
+
set(token) {
|
|
27
|
+
this._memoryToken = token;
|
|
28
|
+
try {
|
|
29
|
+
if (typeof localStorage !== 'undefined') {
|
|
30
|
+
localStorage.setItem(TOKEN_STORAGE_KEY, token);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { /* SSR / test */ }
|
|
34
|
+
},
|
|
35
|
+
clear() {
|
|
36
|
+
this._memoryToken = null;
|
|
37
|
+
try {
|
|
38
|
+
if (typeof localStorage !== 'undefined') {
|
|
39
|
+
localStorage.removeItem(TOKEN_STORAGE_KEY);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch { /* SSR / test */ }
|
|
43
|
+
},
|
|
44
|
+
};
|
|
9
45
|
/**
|
|
10
46
|
* Resolve a baseURL (which may be relative or absolute) into the
|
|
11
47
|
* `{ origin, basePath }` pair required by the better-auth client.
|
|
@@ -37,13 +73,42 @@ function getWindowOrigin() {
|
|
|
37
73
|
}
|
|
38
74
|
return undefined;
|
|
39
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a fetch wrapper that injects Bearer token from localStorage
|
|
78
|
+
* and captures updated tokens from the `set-auth-token` response header
|
|
79
|
+
* (provided by better-auth's server-side bearer plugin).
|
|
80
|
+
*/
|
|
81
|
+
function createBearerFetch(baseFetch) {
|
|
82
|
+
const fetchImpl = baseFetch || globalThis.fetch.bind(globalThis);
|
|
83
|
+
return async (input, init) => {
|
|
84
|
+
const headers = new Headers(init?.headers);
|
|
85
|
+
const token = TokenStorage.get();
|
|
86
|
+
// Only inject Bearer token for API paths to avoid triggering CORS preflight
|
|
87
|
+
// on public endpoints like /.well-known/objectstack
|
|
88
|
+
if (token) {
|
|
89
|
+
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
|
|
90
|
+
if (/\/api\//i.test(url)) {
|
|
91
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const response = await fetchImpl(input, { ...init, headers });
|
|
95
|
+
// Capture rotated tokens from the bearer plugin's response header
|
|
96
|
+
const newToken = response.headers.get('set-auth-token');
|
|
97
|
+
if (newToken) {
|
|
98
|
+
TokenStorage.set(newToken);
|
|
99
|
+
}
|
|
100
|
+
return response;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
40
103
|
/**
|
|
41
104
|
* Create an auth client instance backed by the official better-auth client.
|
|
42
105
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
106
|
+
* Uses Bearer token authentication: tokens are stored in localStorage and
|
|
107
|
+
* sent via `Authorization: Bearer <token>` header on every request. This
|
|
108
|
+
* works across origins (no cookie dependency) and is compatible with mobile
|
|
109
|
+
* clients.
|
|
110
|
+
*
|
|
111
|
+
* Requires the server to have the better-auth `bearer()` plugin enabled.
|
|
47
112
|
*
|
|
48
113
|
* @example
|
|
49
114
|
* ```ts
|
|
@@ -54,11 +119,13 @@ function getWindowOrigin() {
|
|
|
54
119
|
export function createAuthClient(config) {
|
|
55
120
|
const { baseURL, fetchFn } = config;
|
|
56
121
|
const { origin, basePath } = resolveAuthURL(baseURL);
|
|
122
|
+
const bearerFetch = createBearerFetch(fetchFn);
|
|
57
123
|
const betterAuth = createBetterAuthClient({
|
|
58
124
|
baseURL: origin,
|
|
59
125
|
basePath,
|
|
60
126
|
disableDefaultFetchPlugins: true,
|
|
61
|
-
fetchOptions:
|
|
127
|
+
fetchOptions: { customFetchImpl: bearerFetch },
|
|
128
|
+
plugins: [organizationClient()],
|
|
62
129
|
});
|
|
63
130
|
// The better-auth client exposes methods whose TS return types are narrower
|
|
64
131
|
// than the runtime JSON the server actually sends (e.g. `session` on signIn).
|
|
@@ -74,6 +141,10 @@ export function createAuthClient(config) {
|
|
|
74
141
|
throw new Error(error.message ?? `Auth request failed with status ${error.status}`);
|
|
75
142
|
}
|
|
76
143
|
const payload = data;
|
|
144
|
+
// Persist token for cross-origin session persistence
|
|
145
|
+
if (payload.session?.token) {
|
|
146
|
+
TokenStorage.set(payload.session.token);
|
|
147
|
+
}
|
|
77
148
|
return { user: payload.user, session: payload.session };
|
|
78
149
|
},
|
|
79
150
|
async signUp(signUpData) {
|
|
@@ -86,10 +157,14 @@ export function createAuthClient(config) {
|
|
|
86
157
|
throw new Error(error.message ?? `Auth request failed with status ${error.status}`);
|
|
87
158
|
}
|
|
88
159
|
const payload = data;
|
|
160
|
+
if (payload.session?.token) {
|
|
161
|
+
TokenStorage.set(payload.session.token);
|
|
162
|
+
}
|
|
89
163
|
return { user: payload.user, session: payload.session };
|
|
90
164
|
},
|
|
91
165
|
async signOut() {
|
|
92
166
|
const { error } = await betterAuth.signOut();
|
|
167
|
+
TokenStorage.clear();
|
|
93
168
|
if (error) {
|
|
94
169
|
throw new Error(error.message ?? `Auth request failed with status ${error.status}`);
|
|
95
170
|
}
|
|
@@ -99,6 +174,10 @@ export function createAuthClient(config) {
|
|
|
99
174
|
if (error || !data)
|
|
100
175
|
return null;
|
|
101
176
|
const payload = data;
|
|
177
|
+
// Keep localStorage in sync if the server returns a fresh token
|
|
178
|
+
if (payload.session?.token) {
|
|
179
|
+
TokenStorage.set(payload.session.token);
|
|
180
|
+
}
|
|
102
181
|
return { user: payload.user, session: payload.session };
|
|
103
182
|
},
|
|
104
183
|
async forgotPassword(email) {
|
|
@@ -126,5 +205,177 @@ export function createAuthClient(config) {
|
|
|
126
205
|
const raw = data;
|
|
127
206
|
return (raw && typeof raw === 'object' && 'user' in raw ? raw.user : raw);
|
|
128
207
|
},
|
|
208
|
+
async getConfig() {
|
|
209
|
+
const url = `${origin}${basePath}/config`;
|
|
210
|
+
const response = await bearerFetch(url, {
|
|
211
|
+
method: 'GET',
|
|
212
|
+
headers: { 'Content-Type': 'application/json' },
|
|
213
|
+
});
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
throw new Error(`Failed to load auth config (status ${response.status})`);
|
|
216
|
+
}
|
|
217
|
+
const body = (await response.json());
|
|
218
|
+
// Server wraps the payload as `{ success, data }`; tolerate both shapes.
|
|
219
|
+
if (body && typeof body === 'object' && 'data' in body && body.data) {
|
|
220
|
+
return body.data;
|
|
221
|
+
}
|
|
222
|
+
return body;
|
|
223
|
+
},
|
|
224
|
+
async signInWithProvider(providerId, options = {}) {
|
|
225
|
+
const { type = 'social', callbackURL, errorCallbackURL } = options;
|
|
226
|
+
// better-auth handles the redirect to the provider for us.
|
|
227
|
+
if (type === 'oidc') {
|
|
228
|
+
const oauth2 = betterAuth.signIn.oauth2;
|
|
229
|
+
if (!oauth2) {
|
|
230
|
+
throw new Error('OIDC sign-in is not supported by this auth client build');
|
|
231
|
+
}
|
|
232
|
+
const { error } = await oauth2({ providerId, callbackURL, errorCallbackURL });
|
|
233
|
+
if (error) {
|
|
234
|
+
throw new Error(error.message ?? `Auth request failed with status ${error.status}`);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const { error } = await betterAuth.signIn.social({
|
|
239
|
+
provider: providerId,
|
|
240
|
+
callbackURL,
|
|
241
|
+
errorCallbackURL,
|
|
242
|
+
});
|
|
243
|
+
if (error) {
|
|
244
|
+
throw new Error(error.message ?? `Auth request failed with status ${error.status}`);
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
// --- Organization / Workspace methods ---
|
|
248
|
+
async listOrganizations() {
|
|
249
|
+
const { data, error } = await betterAuth.organization.list();
|
|
250
|
+
if (error)
|
|
251
|
+
throw new Error(error.message ?? 'Failed to list organizations');
|
|
252
|
+
return (data ?? []);
|
|
253
|
+
},
|
|
254
|
+
async createOrganization(orgData) {
|
|
255
|
+
const { data, error } = await betterAuth.organization.create({
|
|
256
|
+
name: orgData.name,
|
|
257
|
+
slug: orgData.slug,
|
|
258
|
+
logo: orgData.logo,
|
|
259
|
+
});
|
|
260
|
+
if (error)
|
|
261
|
+
throw new Error(error.message ?? 'Failed to create organization');
|
|
262
|
+
return data;
|
|
263
|
+
},
|
|
264
|
+
async setActiveOrganization(orgId) {
|
|
265
|
+
const { data, error } = await betterAuth.organization.setActive({
|
|
266
|
+
organizationId: orgId,
|
|
267
|
+
});
|
|
268
|
+
if (error)
|
|
269
|
+
throw new Error(error.message ?? 'Failed to set active organization');
|
|
270
|
+
return (data ?? null);
|
|
271
|
+
},
|
|
272
|
+
async getActiveOrganization() {
|
|
273
|
+
// `/organization/get-full-organization` is the endpoint that returns the
|
|
274
|
+
// active organization record in full. `getActiveMember` returns only the
|
|
275
|
+
// current user's member row (organizationId, role) — not the org itself.
|
|
276
|
+
const { data, error } = await betterAuth.organization.getFullOrganization();
|
|
277
|
+
if (error || !data)
|
|
278
|
+
return null;
|
|
279
|
+
return data;
|
|
280
|
+
},
|
|
281
|
+
async getMembers(orgId) {
|
|
282
|
+
const { data, error } = await betterAuth.organization.listMembers({
|
|
283
|
+
query: { organizationId: orgId },
|
|
284
|
+
});
|
|
285
|
+
if (error)
|
|
286
|
+
throw new Error(error.message ?? 'Failed to get members');
|
|
287
|
+
const result = data;
|
|
288
|
+
if (Array.isArray(result))
|
|
289
|
+
return result;
|
|
290
|
+
return (result?.members ?? []);
|
|
291
|
+
},
|
|
292
|
+
async inviteMember(inviteData) {
|
|
293
|
+
const { data, error } = await betterAuth.organization.inviteMember({
|
|
294
|
+
organizationId: inviteData.organizationId,
|
|
295
|
+
email: inviteData.email,
|
|
296
|
+
role: inviteData.role,
|
|
297
|
+
});
|
|
298
|
+
if (error)
|
|
299
|
+
throw new Error(error.message ?? 'Failed to invite member');
|
|
300
|
+
return data;
|
|
301
|
+
},
|
|
302
|
+
async removeMember(removeData) {
|
|
303
|
+
const { error } = await betterAuth.organization.removeMember({
|
|
304
|
+
organizationId: removeData.organizationId,
|
|
305
|
+
memberIdOrUserId: removeData.memberIdOrUserId,
|
|
306
|
+
});
|
|
307
|
+
if (error)
|
|
308
|
+
throw new Error(error.message ?? 'Failed to remove member');
|
|
309
|
+
},
|
|
310
|
+
async updateMemberRole(payload) {
|
|
311
|
+
const { error } = await betterAuth.organization.updateMemberRole({
|
|
312
|
+
organizationId: payload.organizationId,
|
|
313
|
+
memberId: payload.memberId,
|
|
314
|
+
role: payload.role,
|
|
315
|
+
});
|
|
316
|
+
if (error)
|
|
317
|
+
throw new Error(error.message ?? 'Failed to update member role');
|
|
318
|
+
},
|
|
319
|
+
async updateOrganization(orgId, orgData) {
|
|
320
|
+
const { data, error } = await betterAuth.organization.update({
|
|
321
|
+
organizationId: orgId,
|
|
322
|
+
data: orgData,
|
|
323
|
+
});
|
|
324
|
+
if (error)
|
|
325
|
+
throw new Error(error.message ?? 'Failed to update organization');
|
|
326
|
+
return data;
|
|
327
|
+
},
|
|
328
|
+
async deleteOrganization(orgId) {
|
|
329
|
+
const { error } = await betterAuth.organization.delete({
|
|
330
|
+
organizationId: orgId,
|
|
331
|
+
});
|
|
332
|
+
if (error)
|
|
333
|
+
throw new Error(error.message ?? 'Failed to delete organization');
|
|
334
|
+
},
|
|
335
|
+
async leaveOrganization(orgId) {
|
|
336
|
+
const { error } = await betterAuth.organization.leave({
|
|
337
|
+
organizationId: orgId,
|
|
338
|
+
});
|
|
339
|
+
if (error)
|
|
340
|
+
throw new Error(error.message ?? 'Failed to leave organization');
|
|
341
|
+
},
|
|
342
|
+
// --- Invitation methods ---
|
|
343
|
+
async listInvitations(orgId) {
|
|
344
|
+
const { data, error } = await betterAuth.organization.listInvitations({
|
|
345
|
+
query: { organizationId: orgId },
|
|
346
|
+
});
|
|
347
|
+
if (error)
|
|
348
|
+
throw new Error(error.message ?? 'Failed to list invitations');
|
|
349
|
+
return (data ?? []);
|
|
350
|
+
},
|
|
351
|
+
async cancelInvitation(invitationId) {
|
|
352
|
+
const { error } = await betterAuth.organization.cancelInvitation({ invitationId });
|
|
353
|
+
if (error)
|
|
354
|
+
throw new Error(error.message ?? 'Failed to cancel invitation');
|
|
355
|
+
},
|
|
356
|
+
async getInvitation(invitationId) {
|
|
357
|
+
const { data, error } = await betterAuth.organization.getInvitation({
|
|
358
|
+
query: { id: invitationId },
|
|
359
|
+
});
|
|
360
|
+
if (error)
|
|
361
|
+
throw new Error(error.message ?? 'Failed to load invitation');
|
|
362
|
+
return data;
|
|
363
|
+
},
|
|
364
|
+
async acceptInvitation(invitationId) {
|
|
365
|
+
const { error } = await betterAuth.organization.acceptInvitation({ invitationId });
|
|
366
|
+
if (error)
|
|
367
|
+
throw new Error(error.message ?? 'Failed to accept invitation');
|
|
368
|
+
},
|
|
369
|
+
async rejectInvitation(invitationId) {
|
|
370
|
+
const { error } = await betterAuth.organization.rejectInvitation({ invitationId });
|
|
371
|
+
if (error)
|
|
372
|
+
throw new Error(error.message ?? 'Failed to reject invitation');
|
|
373
|
+
},
|
|
374
|
+
async listUserInvitations() {
|
|
375
|
+
const { data, error } = await betterAuth.organization.listUserInvitations();
|
|
376
|
+
if (error)
|
|
377
|
+
throw new Error(error.message ?? 'Failed to list invitations');
|
|
378
|
+
return (data ?? []);
|
|
379
|
+
},
|
|
129
380
|
};
|
|
130
381
|
}
|
|
@@ -5,32 +5,36 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type { AuthClient } from './types';
|
|
9
8
|
/**
|
|
10
9
|
* Options for creating an authenticated adapter.
|
|
11
10
|
*/
|
|
12
11
|
export interface AuthenticatedAdapterOptions {
|
|
13
12
|
/** Base URL for the ObjectStack API */
|
|
14
13
|
baseUrl: string;
|
|
15
|
-
/** Auth client to get session tokens from */
|
|
16
|
-
authClient: AuthClient;
|
|
17
14
|
/** Additional adapter options */
|
|
18
15
|
[key: string]: unknown;
|
|
19
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Get/set the active organization ID for tenant-scoped API requests.
|
|
19
|
+
* Used by createAuthenticatedFetch to inject X-Tenant-ID header.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ActiveOrganizationStorage: {
|
|
22
|
+
_memoryValue: string | null;
|
|
23
|
+
get(): string | null;
|
|
24
|
+
set(orgId: string): void;
|
|
25
|
+
clear(): void;
|
|
26
|
+
};
|
|
20
27
|
/**
|
|
21
28
|
* Creates an authenticated fetch wrapper that injects the Bearer token
|
|
22
|
-
* from
|
|
23
|
-
*
|
|
24
|
-
* This is the recommended way to integrate authentication with
|
|
25
|
-
* @objectstack/client's ObjectStackAdapter.
|
|
29
|
+
* from localStorage into every request to the ObjectStack API.
|
|
30
|
+
* Also injects X-Tenant-ID header when an active organization is set.
|
|
26
31
|
*
|
|
27
32
|
* @example
|
|
28
33
|
* ```ts
|
|
29
34
|
* import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
30
|
-
* import {
|
|
35
|
+
* import { createAuthenticatedFetch } from '@object-ui/auth';
|
|
31
36
|
*
|
|
32
|
-
* const
|
|
33
|
-
* const authenticatedFetch = createAuthenticatedFetch(authClient);
|
|
37
|
+
* const authenticatedFetch = createAuthenticatedFetch();
|
|
34
38
|
*
|
|
35
39
|
* const adapter = new ObjectStackAdapter({
|
|
36
40
|
* baseUrl: '/api/v1',
|
|
@@ -38,5 +42,5 @@ export interface AuthenticatedAdapterOptions {
|
|
|
38
42
|
* });
|
|
39
43
|
* ```
|
|
40
44
|
*/
|
|
41
|
-
export declare function createAuthenticatedFetch(
|
|
45
|
+
export declare function createAuthenticatedFetch(): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
42
46
|
//# sourceMappingURL=createAuthenticatedFetch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAuthenticatedFetch.d.ts","sourceRoot":"","sources":["../src/createAuthenticatedFetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"createAuthenticatedFetch.d.ts","sourceRoot":"","sources":["../src/createAuthenticatedFetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID;;;GAGG;AACH,eAAO,MAAM,yBAAyB;kBACd,MAAM,GAAG,IAAI;WAE5B,MAAM,GAAG,IAAI;eAST,MAAM,GAAG,IAAI;aASf,IAAI;CAQd,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiB9G"}
|
|
@@ -5,20 +5,53 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
+
import { TokenStorage } from './createAuthClient';
|
|
9
|
+
const ACTIVE_ORG_STORAGE_KEY = 'auth-active-organization-id';
|
|
10
|
+
/**
|
|
11
|
+
* Get/set the active organization ID for tenant-scoped API requests.
|
|
12
|
+
* Used by createAuthenticatedFetch to inject X-Tenant-ID header.
|
|
13
|
+
*/
|
|
14
|
+
export const ActiveOrganizationStorage = {
|
|
15
|
+
_memoryValue: null,
|
|
16
|
+
get() {
|
|
17
|
+
try {
|
|
18
|
+
if (typeof localStorage !== 'undefined') {
|
|
19
|
+
return localStorage.getItem(ACTIVE_ORG_STORAGE_KEY);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch { /* SSR / test */ }
|
|
23
|
+
return this._memoryValue;
|
|
24
|
+
},
|
|
25
|
+
set(orgId) {
|
|
26
|
+
this._memoryValue = orgId;
|
|
27
|
+
try {
|
|
28
|
+
if (typeof localStorage !== 'undefined') {
|
|
29
|
+
localStorage.setItem(ACTIVE_ORG_STORAGE_KEY, orgId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch { /* SSR / test */ }
|
|
33
|
+
},
|
|
34
|
+
clear() {
|
|
35
|
+
this._memoryValue = null;
|
|
36
|
+
try {
|
|
37
|
+
if (typeof localStorage !== 'undefined') {
|
|
38
|
+
localStorage.removeItem(ACTIVE_ORG_STORAGE_KEY);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch { /* SSR / test */ }
|
|
42
|
+
},
|
|
43
|
+
};
|
|
8
44
|
/**
|
|
9
45
|
* Creates an authenticated fetch wrapper that injects the Bearer token
|
|
10
|
-
* from
|
|
11
|
-
*
|
|
12
|
-
* This is the recommended way to integrate authentication with
|
|
13
|
-
* @objectstack/client's ObjectStackAdapter.
|
|
46
|
+
* from localStorage into every request to the ObjectStack API.
|
|
47
|
+
* Also injects X-Tenant-ID header when an active organization is set.
|
|
14
48
|
*
|
|
15
49
|
* @example
|
|
16
50
|
* ```ts
|
|
17
51
|
* import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
18
|
-
* import {
|
|
52
|
+
* import { createAuthenticatedFetch } from '@object-ui/auth';
|
|
19
53
|
*
|
|
20
|
-
* const
|
|
21
|
-
* const authenticatedFetch = createAuthenticatedFetch(authClient);
|
|
54
|
+
* const authenticatedFetch = createAuthenticatedFetch();
|
|
22
55
|
*
|
|
23
56
|
* const adapter = new ObjectStackAdapter({
|
|
24
57
|
* baseUrl: '/api/v1',
|
|
@@ -26,12 +59,20 @@
|
|
|
26
59
|
* });
|
|
27
60
|
* ```
|
|
28
61
|
*/
|
|
29
|
-
export function createAuthenticatedFetch(
|
|
62
|
+
export function createAuthenticatedFetch() {
|
|
30
63
|
return async (input, init) => {
|
|
31
|
-
const session = await authClient.getSession();
|
|
32
64
|
const headers = new Headers(init?.headers);
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
const token = TokenStorage.get();
|
|
66
|
+
if (token) {
|
|
67
|
+
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
|
|
68
|
+
if (/\/api\//i.test(url)) {
|
|
69
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Inject tenant header for multi-tenant routing
|
|
73
|
+
const activeOrgId = ActiveOrganizationStorage.get();
|
|
74
|
+
if (activeOrgId) {
|
|
75
|
+
headers.set('X-Tenant-ID', activeOrgId);
|
|
35
76
|
}
|
|
36
77
|
return fetch(input, { ...init, headers });
|
|
37
78
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -25,11 +25,12 @@ export { AuthGuard, type AuthGuardProps } from './AuthGuard';
|
|
|
25
25
|
export { LoginForm, type LoginFormProps, type LoginFormLabels } from './LoginForm';
|
|
26
26
|
export { RegisterForm, type RegisterFormProps, type RegisterFormLabels } from './RegisterForm';
|
|
27
27
|
export { ForgotPasswordForm, type ForgotPasswordFormProps, type ForgotPasswordFormLabels } from './ForgotPasswordForm';
|
|
28
|
+
export { SocialSignInButtons, type SocialSignInButtonsProps } from './SocialSignInButtons';
|
|
28
29
|
export { UserMenu, type UserMenuProps } from './UserMenu';
|
|
29
30
|
export { PreviewBanner, type PreviewBannerProps } from './PreviewBanner';
|
|
30
|
-
export { createAuthClient } from './createAuthClient';
|
|
31
|
-
export { createAuthenticatedFetch, type AuthenticatedAdapterOptions } from './createAuthenticatedFetch';
|
|
31
|
+
export { createAuthClient, TokenStorage } from './createAuthClient';
|
|
32
|
+
export { createAuthenticatedFetch, ActiveOrganizationStorage, type AuthenticatedAdapterOptions } from './createAuthenticatedFetch';
|
|
32
33
|
export { getUserInitials } from './types';
|
|
33
|
-
export type { AuthUser, AuthSession, AuthState, AuthClient, AuthClientConfig, AuthLinkComponentProps, AuthProviderConfig, PreviewModeOptions, SignInCredentials, SignUpData, } from './types';
|
|
34
|
+
export type { AuthUser, AuthSession, AuthState, AuthClient, AuthClientConfig, AuthLinkComponentProps, AuthProviderConfig, PreviewModeOptions, SignInCredentials, SignUpData, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthSocialProvider, AuthPublicConfig, SignInWithProviderOptions, } from './types';
|
|
34
35
|
export type { AuthContextValue } from './AuthContext';
|
|
35
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACvH,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACvH,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,KAAK,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACnI,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C,YAAY,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -25,8 +25,9 @@ export { AuthGuard } from './AuthGuard';
|
|
|
25
25
|
export { LoginForm } from './LoginForm';
|
|
26
26
|
export { RegisterForm } from './RegisterForm';
|
|
27
27
|
export { ForgotPasswordForm } from './ForgotPasswordForm';
|
|
28
|
+
export { SocialSignInButtons } from './SocialSignInButtons';
|
|
28
29
|
export { UserMenu } from './UserMenu';
|
|
29
30
|
export { PreviewBanner } from './PreviewBanner';
|
|
30
|
-
export { createAuthClient } from './createAuthClient';
|
|
31
|
-
export { createAuthenticatedFetch } from './createAuthenticatedFetch';
|
|
31
|
+
export { createAuthClient, TokenStorage } from './createAuthClient';
|
|
32
|
+
export { createAuthenticatedFetch, ActiveOrganizationStorage } from './createAuthenticatedFetch';
|
|
32
33
|
export { getUserInitials } from './types';
|