@meistrari/auth-core 0.1.1 → 1.1.0
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 +444 -0
- package/dist/index.d.mts +5399 -2711
- package/dist/index.d.ts +5399 -2711
- package/dist/index.mjs +501 -27
- package/package.json +6 -4
package/dist/index.mjs
CHANGED
|
@@ -1,29 +1,12 @@
|
|
|
1
1
|
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
2
|
-
import { createAuthClient
|
|
3
|
-
import { organizationClient } from 'better-auth/client/plugins';
|
|
2
|
+
import { createAuthClient } from 'better-auth/client';
|
|
3
|
+
import { organizationClient, twoFactorClient, jwtClient, adminClient, inferAdditionalFields } from 'better-auth/client/plugins';
|
|
4
4
|
import { ssoClient } from '@better-auth/sso/client';
|
|
5
5
|
import { createAccessControl } from 'better-auth/plugins/access';
|
|
6
6
|
import { defaultStatements } from 'better-auth/plugins/organization/access';
|
|
7
7
|
|
|
8
|
-
const version = "
|
|
8
|
+
const version = "1.1.0";
|
|
9
9
|
|
|
10
|
-
function isTokenExpired(token) {
|
|
11
|
-
const payload = JSON.parse(atob(token.split(".")[1] ?? ""));
|
|
12
|
-
return payload.exp && Date.now() / 1e3 > payload.exp;
|
|
13
|
-
}
|
|
14
|
-
async function validateToken(token, apiUrl) {
|
|
15
|
-
try {
|
|
16
|
-
const JWKS = createRemoteJWKSet(
|
|
17
|
-
new URL(`${apiUrl}/api/auth/jwks`)
|
|
18
|
-
);
|
|
19
|
-
await jwtVerify(token, JWKS, {
|
|
20
|
-
issuer: apiUrl
|
|
21
|
-
});
|
|
22
|
-
return true;
|
|
23
|
-
} catch (error) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
10
|
const statements = {
|
|
28
11
|
...defaultStatements,
|
|
29
12
|
access: ["admin", "member", "reviewer"]
|
|
@@ -49,15 +32,54 @@ const rolesAccessControl = {
|
|
|
49
32
|
access: ["reviewer"]
|
|
50
33
|
})
|
|
51
34
|
};
|
|
35
|
+
const userAdditionalFields = {
|
|
36
|
+
lastActiveAt: {
|
|
37
|
+
type: "date",
|
|
38
|
+
required: false,
|
|
39
|
+
defaultValue: null
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const organizationAdditionalFields = {
|
|
43
|
+
settings: {
|
|
44
|
+
type: "json",
|
|
45
|
+
input: true,
|
|
46
|
+
required: false
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
class BaseError extends Error {
|
|
51
|
+
code;
|
|
52
|
+
constructor(code, message, options) {
|
|
53
|
+
super(message, options);
|
|
54
|
+
this.code = code;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class InvalidSocialProvider extends BaseError {
|
|
59
|
+
constructor(message) {
|
|
60
|
+
super("INVALID_SOCIAL_PROVIDER", message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
class InvalidCallbackURL extends BaseError {
|
|
64
|
+
constructor(message) {
|
|
65
|
+
super("INVALID_CALLBACK_URL", message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class EmailRequired extends BaseError {
|
|
69
|
+
constructor(message) {
|
|
70
|
+
super("EMAIL_REQUIRED", message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
const customEndpointsPluginClient = () => {
|
|
53
75
|
return {
|
|
54
76
|
id: "custom-endpoints",
|
|
55
77
|
$InferServerPlugin: {}
|
|
56
78
|
};
|
|
57
79
|
};
|
|
58
|
-
function
|
|
59
|
-
return createAuthClient
|
|
60
|
-
baseURL,
|
|
80
|
+
function createAPIClient(apiUrl, headers) {
|
|
81
|
+
return createAuthClient({
|
|
82
|
+
baseURL: apiUrl,
|
|
61
83
|
plugins: [
|
|
62
84
|
organizationClient({
|
|
63
85
|
ac,
|
|
@@ -65,17 +87,469 @@ function createAuthClient(baseURL) {
|
|
|
65
87
|
teams: {
|
|
66
88
|
enabled: true
|
|
67
89
|
}
|
|
90
|
+
// TODO: check if this is fixed in the next version
|
|
91
|
+
// schema: inferOrgAdditionalFields({
|
|
92
|
+
// organization: {
|
|
93
|
+
// additionalFields: {
|
|
94
|
+
// settings: {
|
|
95
|
+
// type: 'json' as const,
|
|
96
|
+
// input: true,
|
|
97
|
+
// required: false,
|
|
98
|
+
// }
|
|
99
|
+
// },
|
|
100
|
+
// }
|
|
101
|
+
// })
|
|
68
102
|
}),
|
|
69
103
|
customEndpointsPluginClient(),
|
|
70
|
-
ssoClient()
|
|
104
|
+
ssoClient(),
|
|
105
|
+
twoFactorClient(),
|
|
106
|
+
jwtClient(),
|
|
107
|
+
adminClient(),
|
|
108
|
+
inferAdditionalFields({
|
|
109
|
+
user: userAdditionalFields
|
|
110
|
+
})
|
|
71
111
|
],
|
|
72
112
|
fetchOptions: {
|
|
73
113
|
credentials: "include",
|
|
74
114
|
headers: {
|
|
75
|
-
"User-Agent": `auth-sdk:${version}
|
|
76
|
-
|
|
115
|
+
"User-Agent": `auth-sdk:core:${version}`,
|
|
116
|
+
...headers
|
|
117
|
+
},
|
|
118
|
+
throw: true
|
|
77
119
|
}
|
|
78
120
|
});
|
|
79
121
|
}
|
|
122
|
+
createAPIClient("");
|
|
123
|
+
|
|
124
|
+
class OrganizationService {
|
|
125
|
+
/**
|
|
126
|
+
* Creates a new OrganizationService instance.
|
|
127
|
+
*
|
|
128
|
+
* @param client - The API client for making organization requests
|
|
129
|
+
*/
|
|
130
|
+
constructor(client) {
|
|
131
|
+
this.client = client;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Retrieves a single organization by ID with optional related data.
|
|
135
|
+
*
|
|
136
|
+
* @param id - The organization ID
|
|
137
|
+
* @param options - Configuration for including related data
|
|
138
|
+
* @param options.includeMembers - Include organization members
|
|
139
|
+
* @param options.includeInvitations - Include pending invitations
|
|
140
|
+
* @param options.includeTeams - Include organization teams
|
|
141
|
+
* @returns The organization with optionally included related data
|
|
142
|
+
*/
|
|
143
|
+
async getOrganization(id, options) {
|
|
144
|
+
const organizationPromise = this.client.organization.list({
|
|
145
|
+
query: {
|
|
146
|
+
id
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
const membersPromise = options?.includeMembers ? this.client.organization.listMembers({
|
|
150
|
+
query: {
|
|
151
|
+
organizationId: id
|
|
152
|
+
}
|
|
153
|
+
}) : void 0;
|
|
154
|
+
const invitationsPromise = options?.includeInvitations ? this.client.organization.listInvitations({
|
|
155
|
+
query: {
|
|
156
|
+
organizationId: id
|
|
157
|
+
}
|
|
158
|
+
}) : void 0;
|
|
159
|
+
const teamsPromise = options?.includeTeams ? this.client.organization.listTeams({
|
|
160
|
+
query: {
|
|
161
|
+
organizationId: id
|
|
162
|
+
}
|
|
163
|
+
}) : void 0;
|
|
164
|
+
const [organization, members, invitations, teams] = await Promise.all([organizationPromise, membersPromise, invitationsPromise, teamsPromise]);
|
|
165
|
+
return {
|
|
166
|
+
...organization[0],
|
|
167
|
+
...members && { members: members.members },
|
|
168
|
+
...invitations && { invitations },
|
|
169
|
+
...teams && { teams }
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Lists all organizations accessible to the current user.
|
|
174
|
+
*
|
|
175
|
+
* @returns An array of organizations
|
|
176
|
+
*/
|
|
177
|
+
async listOrganizations() {
|
|
178
|
+
const organizations = await this.client.organization.list();
|
|
179
|
+
return organizations;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Sets the active organization for the current user session.
|
|
183
|
+
*
|
|
184
|
+
* @param id - The organization ID to set as active
|
|
185
|
+
*/
|
|
186
|
+
async setActiveOrganization(id) {
|
|
187
|
+
await this.client.organization.setActive({
|
|
188
|
+
organizationId: id
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Updates an organization's details.
|
|
193
|
+
*
|
|
194
|
+
* @param payload - The organization fields to update
|
|
195
|
+
* @param payload.name - New organization name
|
|
196
|
+
* @param payload.logo - New organization logo URL
|
|
197
|
+
* @param payload.settings - New organization settings
|
|
198
|
+
* @returns The updated organization
|
|
199
|
+
*/
|
|
200
|
+
async updateOrganization(payload) {
|
|
201
|
+
const organization = await this.client.organization.update({
|
|
202
|
+
data: {
|
|
203
|
+
...payload,
|
|
204
|
+
logo: payload.logo ?? void 0
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return organization;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Lists members of the active organization with optional pagination.
|
|
211
|
+
*
|
|
212
|
+
* @param options - Pagination options
|
|
213
|
+
* @param options.limit - Maximum number of members to return
|
|
214
|
+
* @param options.offset - Number of members to skip
|
|
215
|
+
* @returns An array of organization members
|
|
216
|
+
*/
|
|
217
|
+
async listMembers(options) {
|
|
218
|
+
const members = await this.client.organization.listMembers({
|
|
219
|
+
query: options
|
|
220
|
+
});
|
|
221
|
+
return members.members;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Gets the currently active organization member for the authenticated user.
|
|
225
|
+
*
|
|
226
|
+
* @returns The active member object
|
|
227
|
+
*/
|
|
228
|
+
async getActiveMember() {
|
|
229
|
+
return this.client.organization.getActiveMember();
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Invites a user to join the active organization.
|
|
233
|
+
*
|
|
234
|
+
* @param options - Invitation configuration
|
|
235
|
+
* @param options.userEmail - Email address of the user to invite
|
|
236
|
+
* @param options.role - Role to assign to the invited user
|
|
237
|
+
* @param options.teamId - Team ID to add the user to
|
|
238
|
+
* @param options.resend - Whether to resend if invitation already exists
|
|
239
|
+
* @returns The created invitation
|
|
240
|
+
*/
|
|
241
|
+
async inviteUserToOrganization({ userEmail, role, teamId, resend }) {
|
|
242
|
+
return this.client.organization.inviteMember({
|
|
243
|
+
email: userEmail,
|
|
244
|
+
role,
|
|
245
|
+
teamId,
|
|
246
|
+
resend: resend ?? false
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Cancels a pending organization invitation.
|
|
251
|
+
*
|
|
252
|
+
* @param id - The invitation ID to cancel
|
|
253
|
+
*/
|
|
254
|
+
async cancelInvitation(id) {
|
|
255
|
+
await this.client.organization.cancelInvitation({
|
|
256
|
+
invitationId: id
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Accepts an organization invitation.
|
|
261
|
+
*
|
|
262
|
+
* @param id - The invitation ID to accept
|
|
263
|
+
*/
|
|
264
|
+
async acceptInvitation(id) {
|
|
265
|
+
await this.client.organization.acceptInvitation({
|
|
266
|
+
invitationId: id
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Removes a user from the active organization.
|
|
271
|
+
*
|
|
272
|
+
* @param options - User identifier (either memberId or userEmail must be provided)
|
|
273
|
+
* @param options.memberId - The member ID to remove
|
|
274
|
+
* @param options.userEmail - The user email to remove
|
|
275
|
+
|
|
276
|
+
*/
|
|
277
|
+
async removeUserFromOrganization({ memberId, userEmail }) {
|
|
278
|
+
await this.client.organization.removeMember({
|
|
279
|
+
memberIdOrEmail: memberId ?? userEmail
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Updates the role of an organization member.
|
|
284
|
+
*
|
|
285
|
+
* @param options - Role update configuration
|
|
286
|
+
* @param options.memberId - The member ID to update
|
|
287
|
+
* @param options.role - The new role to assign
|
|
288
|
+
*/
|
|
289
|
+
async updateMemberRole({ memberId, role }) {
|
|
290
|
+
await this.client.organization.updateMemberRole({
|
|
291
|
+
memberId,
|
|
292
|
+
role
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Creates a new team within the active organization.
|
|
297
|
+
*
|
|
298
|
+
* @param payload - Team configuration
|
|
299
|
+
* @param payload.name - The name of the team
|
|
300
|
+
* @returns The created team
|
|
301
|
+
*/
|
|
302
|
+
async createTeam(payload) {
|
|
303
|
+
return this.client.organization.createTeam(payload);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Updates an existing team's details.
|
|
307
|
+
*
|
|
308
|
+
* @param id - The team ID to update
|
|
309
|
+
* @param payload - Team fields to update
|
|
310
|
+
* @param payload.name - The new team name
|
|
311
|
+
* @returns The updated team
|
|
312
|
+
*/
|
|
313
|
+
async updateTeam(id, payload) {
|
|
314
|
+
return this.client.organization.updateTeam({
|
|
315
|
+
teamId: id,
|
|
316
|
+
data: payload
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Deletes a team from the active organization.
|
|
321
|
+
*
|
|
322
|
+
* @param id - The team ID to delete
|
|
323
|
+
*/
|
|
324
|
+
async deleteTeam(id) {
|
|
325
|
+
await this.client.organization.removeTeam({
|
|
326
|
+
teamId: id
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Lists all teams in the active organization.
|
|
331
|
+
*
|
|
332
|
+
* @returns An array of teams
|
|
333
|
+
*/
|
|
334
|
+
async listTeams() {
|
|
335
|
+
return this.client.organization.listTeams();
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Lists all members of a specific team.
|
|
339
|
+
*
|
|
340
|
+
* @param id - The team ID
|
|
341
|
+
* @returns An array of team members
|
|
342
|
+
*/
|
|
343
|
+
async listTeamMembers(id) {
|
|
344
|
+
return this.client.organization.listTeamMembers({
|
|
345
|
+
query: {
|
|
346
|
+
teamId: id
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Adds a user to a team.
|
|
352
|
+
*
|
|
353
|
+
* @param teamId - The team ID
|
|
354
|
+
* @param userId - The user ID to add
|
|
355
|
+
*/
|
|
356
|
+
async addTeamMember(teamId, userId) {
|
|
357
|
+
await this.client.organization.addTeamMember({
|
|
358
|
+
teamId,
|
|
359
|
+
userId
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Removes a user from a team.
|
|
364
|
+
*
|
|
365
|
+
* @param teamId - The team ID
|
|
366
|
+
* @param userId - The user ID to remove
|
|
367
|
+
*/
|
|
368
|
+
async removeTeamMember(teamId, userId) {
|
|
369
|
+
await this.client.organization.removeTeamMember({
|
|
370
|
+
teamId,
|
|
371
|
+
userId
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function isValidUrl(url) {
|
|
377
|
+
try {
|
|
378
|
+
new URL(url);
|
|
379
|
+
return true;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
class SessionService {
|
|
385
|
+
/**
|
|
386
|
+
* Creates a new SessionService instance.
|
|
387
|
+
*
|
|
388
|
+
* @param client - The API client for making authentication requests
|
|
389
|
+
* @param apiUrl - The base URL of the authentication API
|
|
390
|
+
*/
|
|
391
|
+
constructor(client, apiUrl) {
|
|
392
|
+
this.client = client;
|
|
393
|
+
this.apiUrl = apiUrl;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Initiates social authentication flow with Google or Microsoft.
|
|
397
|
+
*
|
|
398
|
+
* @param options - Social sign-in configuration
|
|
399
|
+
* @param options.provider - The social provider
|
|
400
|
+
* @param options.callbackURL - URL to redirect to after successful authentication
|
|
401
|
+
* @param options.errorCallbackURL - URL to redirect to on error
|
|
402
|
+
*
|
|
403
|
+
* @throws {InvalidSocialProvider} When the provider is not 'google' or 'microsoft'
|
|
404
|
+
* @throws {InvalidCallbackURL} When callback URLs are malformed
|
|
405
|
+
*/
|
|
406
|
+
async signInWithSocialProvider({
|
|
407
|
+
provider,
|
|
408
|
+
callbackURL,
|
|
409
|
+
errorCallbackURL
|
|
410
|
+
}) {
|
|
411
|
+
if (provider !== "google" && provider !== "microsoft") {
|
|
412
|
+
throw new InvalidSocialProvider("Invalid social provider");
|
|
413
|
+
}
|
|
414
|
+
if (!isValidUrl(callbackURL)) {
|
|
415
|
+
throw new InvalidCallbackURL(`Invalid callback URL: ${callbackURL}`);
|
|
416
|
+
}
|
|
417
|
+
if (errorCallbackURL && !isValidUrl(errorCallbackURL)) {
|
|
418
|
+
throw new InvalidCallbackURL(`Invalid error callback URL: ${errorCallbackURL}`);
|
|
419
|
+
}
|
|
420
|
+
const appUrl = encodeURIComponent(new URL(callbackURL).origin);
|
|
421
|
+
await this.client.signIn.social({
|
|
422
|
+
provider,
|
|
423
|
+
callbackURL,
|
|
424
|
+
errorCallbackURL: errorCallbackURL ?? `${this.apiUrl}/error?callbackURL=${appUrl}`
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Initiates SAML-based Single Sign-On authentication flow.
|
|
429
|
+
*
|
|
430
|
+
* @param options - SAML sign-in configuration
|
|
431
|
+
* @param options.email - User's email to determine the SAML provider
|
|
432
|
+
* @param options.callbackURL - URL to redirect to after successful authentication
|
|
433
|
+
* @param options.errorCallbackURL - URL to redirect to on error
|
|
434
|
+
*
|
|
435
|
+
* @throws {EmailRequired} When email is not provided
|
|
436
|
+
* @throws {InvalidCallbackURL} When callback URLs are malformed
|
|
437
|
+
*/
|
|
438
|
+
async signInWithSaml({
|
|
439
|
+
email,
|
|
440
|
+
callbackURL,
|
|
441
|
+
errorCallbackURL
|
|
442
|
+
}) {
|
|
443
|
+
if (!email) {
|
|
444
|
+
throw new EmailRequired("Email is required");
|
|
445
|
+
}
|
|
446
|
+
if (!isValidUrl(callbackURL)) {
|
|
447
|
+
throw new InvalidCallbackURL(`Invalid callback URL: ${callbackURL}`);
|
|
448
|
+
}
|
|
449
|
+
if (errorCallbackURL && !isValidUrl(errorCallbackURL)) {
|
|
450
|
+
throw new InvalidCallbackURL(`Invalid error callback URL: ${errorCallbackURL}`);
|
|
451
|
+
}
|
|
452
|
+
const appUrl = encodeURIComponent(new URL(callbackURL).origin);
|
|
453
|
+
await this.client.signIn.sso({
|
|
454
|
+
email,
|
|
455
|
+
callbackURL,
|
|
456
|
+
errorCallbackURL: errorCallbackURL ?? `${this.apiUrl}/error?callbackURL=${appUrl}`,
|
|
457
|
+
providerType: "saml"
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Authenticates a user with email and password credentials.
|
|
462
|
+
*
|
|
463
|
+
* @param options - Email/password sign-in configuration
|
|
464
|
+
* @param options.email - User's email address
|
|
465
|
+
* @param options.password - User's password
|
|
466
|
+
*/
|
|
467
|
+
async signInWithEmailAndPassword({
|
|
468
|
+
email,
|
|
469
|
+
password
|
|
470
|
+
}) {
|
|
471
|
+
await this.client.signIn.email({
|
|
472
|
+
email,
|
|
473
|
+
password
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Signs out the currently authenticated user.
|
|
478
|
+
*
|
|
479
|
+
* @param callback - Callback function to execute after signing out
|
|
480
|
+
*/
|
|
481
|
+
async signOut(callback) {
|
|
482
|
+
await this.client.signOut();
|
|
483
|
+
await callback?.();
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Initiates a password reset request by sending a reset email.
|
|
487
|
+
*
|
|
488
|
+
* @param email - Email address of the user requesting password reset
|
|
489
|
+
* @param callbackURL - URL where the user will be redirected to complete the reset
|
|
490
|
+
*/
|
|
491
|
+
async requestPasswordReset(email, callbackURL) {
|
|
492
|
+
await this.client.requestPasswordReset({
|
|
493
|
+
email,
|
|
494
|
+
redirectTo: callbackURL
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Completes the password reset process with a reset token and new password.
|
|
499
|
+
*
|
|
500
|
+
* @param token - The password reset token from the email
|
|
501
|
+
* @param password - The new password to set
|
|
502
|
+
*/
|
|
503
|
+
async resetPassword(token, password) {
|
|
504
|
+
await this.client.resetPassword({
|
|
505
|
+
token,
|
|
506
|
+
newPassword: password
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
class AuthClient {
|
|
512
|
+
client;
|
|
513
|
+
/**
|
|
514
|
+
* Session management service for authentication operations
|
|
515
|
+
*/
|
|
516
|
+
session;
|
|
517
|
+
/**
|
|
518
|
+
* Organization management service for multi-tenant operations
|
|
519
|
+
*/
|
|
520
|
+
organization;
|
|
521
|
+
/**
|
|
522
|
+
* Creates a new AuthClient instance.
|
|
523
|
+
*
|
|
524
|
+
* @param apiUrl - The base URL of the authentication API
|
|
525
|
+
* @param headers - Custom headers to include in all API requests
|
|
526
|
+
*/
|
|
527
|
+
constructor(apiUrl, headers) {
|
|
528
|
+
this.client = createAPIClient(apiUrl, headers);
|
|
529
|
+
this.session = new SessionService(this.client, apiUrl);
|
|
530
|
+
this.organization = new OrganizationService(this.client);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function isTokenExpired(token) {
|
|
535
|
+
const payload = JSON.parse(atob(token.split(".")[1] ?? ""));
|
|
536
|
+
return payload.exp && Date.now() / 1e3 > payload.exp;
|
|
537
|
+
}
|
|
538
|
+
async function validateToken(token, apiUrl) {
|
|
539
|
+
if (isTokenExpired(token)) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const JWKS = createRemoteJWKSet(
|
|
544
|
+
new URL(`${apiUrl}/api/auth/jwks`)
|
|
545
|
+
);
|
|
546
|
+
await jwtVerify(token, JWKS, {
|
|
547
|
+
issuer: apiUrl
|
|
548
|
+
});
|
|
549
|
+
return true;
|
|
550
|
+
} catch (error) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
80
554
|
|
|
81
|
-
export { Roles, ac,
|
|
555
|
+
export { AuthClient, Roles, ac, isTokenExpired, organizationAdditionalFields, rolesAccessControl, userAdditionalFields, validateToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/auth-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
"build": "unbuild"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@better-auth/sso": "1.
|
|
21
|
-
"better-auth": "1.
|
|
22
|
-
"jose": "6.1.0"
|
|
20
|
+
"@better-auth/sso": "1.4.1",
|
|
21
|
+
"better-auth": "1.4.1",
|
|
22
|
+
"jose": "6.1.0",
|
|
23
|
+
"nanostores": "1.0.1",
|
|
24
|
+
"@better-fetch/fetch": "1.1.18"
|
|
23
25
|
},
|
|
24
26
|
"devDependencies": {
|
|
25
27
|
"@types/node": "latest",
|