@meistrari/auth-core 1.20.1 → 1.21.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/dist/index.d.mts +794 -60
- package/dist/index.d.ts +794 -60
- package/dist/index.mjs +449 -30
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2,13 +2,75 @@ import { decodeJwt, createRemoteJWKSet, jwtVerify } from 'jose';
|
|
|
2
2
|
import { apiKeyClient } from '@better-auth/api-key/client';
|
|
3
3
|
import { ssoClient } from '@better-auth/sso/client';
|
|
4
4
|
import { createAuthClient } from 'better-auth/client';
|
|
5
|
-
import { organizationClient, inferOrgAdditionalFields, twoFactorClient, jwtClient, adminClient, inferAdditionalFields } from 'better-auth/client/plugins';
|
|
5
|
+
import { organizationClient, inferOrgAdditionalFields, twoFactorClient, jwtClient, adminClient, inferAdditionalFields, emailOTPClient } from 'better-auth/client/plugins';
|
|
6
6
|
import { createAccessControl } from 'better-auth/plugins/access';
|
|
7
7
|
import { defaultStatements } from 'better-auth/plugins/organization/access';
|
|
8
|
-
import z
|
|
8
|
+
import z, { z as z$1 } from 'zod';
|
|
9
9
|
export { APIError } from 'better-auth';
|
|
10
10
|
|
|
11
|
-
const version = "1.
|
|
11
|
+
const version = "1.21.0";
|
|
12
|
+
|
|
13
|
+
function parsedEnumValues(schema) {
|
|
14
|
+
return Object.fromEntries(
|
|
15
|
+
Object.entries(schema.enum).map(([key, value]) => [
|
|
16
|
+
key,
|
|
17
|
+
schema.parse(value)
|
|
18
|
+
])
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const AMRBrand = Symbol("amr");
|
|
23
|
+
const AALBrand = Symbol("aal");
|
|
24
|
+
const ACRBrand = Symbol("acr");
|
|
25
|
+
const AuthMethodBrand = Symbol("authMethod");
|
|
26
|
+
const AMR = z.enum({
|
|
27
|
+
Pwd: "pwd",
|
|
28
|
+
Otp: "otp",
|
|
29
|
+
Oauth: "oauth",
|
|
30
|
+
Sso: "sso",
|
|
31
|
+
Saml: "saml"
|
|
32
|
+
}).brand(AMRBrand);
|
|
33
|
+
const AAL = z.enum({
|
|
34
|
+
Aal1: "aal1",
|
|
35
|
+
Aal2: "aal2",
|
|
36
|
+
Aal3: "aal3"
|
|
37
|
+
}).brand(AALBrand);
|
|
38
|
+
const ACR = z.enum({
|
|
39
|
+
Unspecified: "urn:tela:auth:unspecified"
|
|
40
|
+
}).brand(ACRBrand);
|
|
41
|
+
const AuthMethod = z.enum({
|
|
42
|
+
Password: "password",
|
|
43
|
+
EmailOTP: "email_otp",
|
|
44
|
+
OAuth: "oauth",
|
|
45
|
+
SAML: "saml",
|
|
46
|
+
Unknown: "unknown"
|
|
47
|
+
}).brand(AuthMethodBrand);
|
|
48
|
+
const AMRs = parsedEnumValues(AMR);
|
|
49
|
+
const AALs = parsedEnumValues(AAL);
|
|
50
|
+
const ACRs = parsedEnumValues(ACR);
|
|
51
|
+
const AuthMethods = parsedEnumValues(AuthMethod);
|
|
52
|
+
const assuranceFields = {
|
|
53
|
+
authMethod: {
|
|
54
|
+
type: "string",
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
amr: {
|
|
58
|
+
type: "json",
|
|
59
|
+
required: true
|
|
60
|
+
},
|
|
61
|
+
aal: {
|
|
62
|
+
type: "string",
|
|
63
|
+
required: true
|
|
64
|
+
},
|
|
65
|
+
acr: {
|
|
66
|
+
type: "string",
|
|
67
|
+
required: true
|
|
68
|
+
},
|
|
69
|
+
assuredAt: {
|
|
70
|
+
type: "date",
|
|
71
|
+
required: true
|
|
72
|
+
}
|
|
73
|
+
};
|
|
12
74
|
|
|
13
75
|
const statements = {
|
|
14
76
|
...defaultStatements,
|
|
@@ -68,21 +130,36 @@ const invitationAdditionalFields = {
|
|
|
68
130
|
required: false
|
|
69
131
|
}
|
|
70
132
|
};
|
|
71
|
-
const JWTPayloadUser = z.object({
|
|
72
|
-
id: z.string(),
|
|
73
|
-
name: z.string(),
|
|
74
|
-
image: z.string().nullable().optional(),
|
|
75
|
-
role: z.string().nullable()
|
|
133
|
+
const JWTPayloadUser = z$1.object({
|
|
134
|
+
id: z$1.string(),
|
|
135
|
+
name: z$1.string(),
|
|
136
|
+
image: z$1.string().nullable().optional(),
|
|
137
|
+
role: z$1.string().nullable()
|
|
138
|
+
});
|
|
139
|
+
const JWTPayloadWorkspace = z$1.object({
|
|
140
|
+
id: z$1.string(),
|
|
141
|
+
title: z$1.string()
|
|
76
142
|
});
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
143
|
+
const JWTPayloadAssurance = z$1.object({
|
|
144
|
+
authMethod: AuthMethod,
|
|
145
|
+
amr: AMR.array(),
|
|
146
|
+
aal: AAL,
|
|
147
|
+
acr: ACR,
|
|
148
|
+
assuredAt: z$1.string().datetime()
|
|
80
149
|
});
|
|
81
|
-
const
|
|
82
|
-
|
|
150
|
+
const DEFAULT_JWT_ASSURANCE = {
|
|
151
|
+
authMethod: AuthMethods.Unknown,
|
|
152
|
+
amr: [],
|
|
153
|
+
aal: AALs.Aal1,
|
|
154
|
+
acr: ACRs.Unspecified,
|
|
155
|
+
assuredAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
156
|
+
};
|
|
157
|
+
const JWTPayload = z$1.object({
|
|
158
|
+
email: z$1.string(),
|
|
83
159
|
user: JWTPayloadUser,
|
|
84
160
|
workspace: JWTPayloadWorkspace,
|
|
85
|
-
sessionKey: z.string()
|
|
161
|
+
sessionKey: z$1.string(),
|
|
162
|
+
assurance: JWTPayloadAssurance.optional().default(DEFAULT_JWT_ASSURANCE)
|
|
86
163
|
});
|
|
87
164
|
|
|
88
165
|
const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
@@ -227,6 +304,34 @@ function applicationsPluginClient() {
|
|
|
227
304
|
};
|
|
228
305
|
}
|
|
229
306
|
|
|
307
|
+
function sessionAssurancePluginClient() {
|
|
308
|
+
return {
|
|
309
|
+
id: "session-assurance",
|
|
310
|
+
getActions: ($fetch) => ({
|
|
311
|
+
sessionAssurance: {
|
|
312
|
+
getSessionAssurance: async () => {
|
|
313
|
+
return await $fetch("/session-assurance");
|
|
314
|
+
},
|
|
315
|
+
stepUpWithPassword: async (body) => {
|
|
316
|
+
return await $fetch("/session-assurance/step-up/password", { method: "POST", body });
|
|
317
|
+
},
|
|
318
|
+
sendOtp: async () => {
|
|
319
|
+
return await $fetch("/session-assurance/step-up/otp/send", { method: "POST" });
|
|
320
|
+
},
|
|
321
|
+
verifyOtp: async (body) => {
|
|
322
|
+
return await $fetch("/session-assurance/step-up/otp/verify", { method: "POST", body });
|
|
323
|
+
},
|
|
324
|
+
beginOAuthStepUp: async (body) => {
|
|
325
|
+
return await $fetch("/session-assurance/step-up/oauth/begin", { method: "POST", body });
|
|
326
|
+
},
|
|
327
|
+
completeOAuthStepUp: async (body) => {
|
|
328
|
+
return await $fetch("/session-assurance/step-up/oauth/complete", { method: "POST", body });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
230
335
|
function customEndpointsPluginClient() {
|
|
231
336
|
return {
|
|
232
337
|
id: "custom-endpoints",
|
|
@@ -263,7 +368,9 @@ function createAPIClient(apiUrl, fetchOptions = {}) {
|
|
|
263
368
|
inferAdditionalFields({
|
|
264
369
|
user: userAdditionalFields
|
|
265
370
|
}),
|
|
266
|
-
applicationsPluginClient()
|
|
371
|
+
applicationsPluginClient(),
|
|
372
|
+
sessionAssurancePluginClient(),
|
|
373
|
+
emailOTPClient()
|
|
267
374
|
],
|
|
268
375
|
fetchOptions: {
|
|
269
376
|
...fetchOptions,
|
|
@@ -308,6 +415,20 @@ class AuthorizationFlowError extends ApplicationError {
|
|
|
308
415
|
this.code = "AUTHORIZATION_FLOW_ERROR";
|
|
309
416
|
}
|
|
310
417
|
}
|
|
418
|
+
class ApplicationSessionAssuranceError extends ApplicationError {
|
|
419
|
+
payload;
|
|
420
|
+
currentAssurance;
|
|
421
|
+
requiredAssurance;
|
|
422
|
+
validStepUps;
|
|
423
|
+
constructor(payload = null, options) {
|
|
424
|
+
super("Session assurance does not meet application policy. Start a new device authorization flow.", options);
|
|
425
|
+
this.code = "APPLICATION_SESSION_ASSURANCE_NOT_MET";
|
|
426
|
+
this.payload = payload;
|
|
427
|
+
this.currentAssurance = payload?.currentAssurance ?? null;
|
|
428
|
+
this.requiredAssurance = payload?.requiredAssurance ?? null;
|
|
429
|
+
this.validStepUps = payload?.validStepUps ?? [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
311
432
|
class UserNotLoggedInError extends ApplicationError {
|
|
312
433
|
constructor(message, options) {
|
|
313
434
|
super(message, options);
|
|
@@ -345,6 +466,174 @@ class DeviceTransientServerError extends ApplicationError {
|
|
|
345
466
|
}
|
|
346
467
|
}
|
|
347
468
|
|
|
469
|
+
const OAuthStepUpProvider = z.enum(["google", "microsoft"]);
|
|
470
|
+
const StepUpOption = z.discriminatedUnion("method", [
|
|
471
|
+
z.object({ method: z.literal(AMRs.Pwd) }),
|
|
472
|
+
z.object({ method: z.literal(AMRs.Otp) }),
|
|
473
|
+
z.object({
|
|
474
|
+
method: z.literal(AMRs.Oauth),
|
|
475
|
+
providers: OAuthStepUpProvider.array().min(1)
|
|
476
|
+
})
|
|
477
|
+
]);
|
|
478
|
+
const SessionAssurancePolicy = z.object({
|
|
479
|
+
minAal: AAL,
|
|
480
|
+
acceptedAmr: AMR.array().min(1)
|
|
481
|
+
});
|
|
482
|
+
const SENSITIVE_ACTION_ASSURANCE_POLICY = {
|
|
483
|
+
minAal: AALs.Aal2,
|
|
484
|
+
acceptedAmr: [AMRs.Pwd, AMRs.Otp, AMRs.Oauth]
|
|
485
|
+
};
|
|
486
|
+
function getAalRank(aal) {
|
|
487
|
+
switch (aal) {
|
|
488
|
+
case AALs.Aal3:
|
|
489
|
+
return 3;
|
|
490
|
+
case AALs.Aal2:
|
|
491
|
+
return 2;
|
|
492
|
+
case AALs.Aal1:
|
|
493
|
+
return 1;
|
|
494
|
+
default:
|
|
495
|
+
return 0;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function maxAal(left, right) {
|
|
499
|
+
return getAalRank(left) >= getAalRank(right) ? left : right;
|
|
500
|
+
}
|
|
501
|
+
function isAssuranceSufficientForPolicy(assurance, policy) {
|
|
502
|
+
if (!assurance) {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
if (getAalRank(assurance.aal) < getAalRank(policy.minAal)) {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
if (!assurance.amr.length) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
return assurance.amr.some((amr) => policy.acceptedAmr.includes(amr));
|
|
512
|
+
}
|
|
513
|
+
function canStepUpWithAmr(assurance, amr) {
|
|
514
|
+
return !assurance.amr.includes(amr);
|
|
515
|
+
}
|
|
516
|
+
function mergeStepUpAssurance(assurance, amr, now = /* @__PURE__ */ new Date()) {
|
|
517
|
+
if (!canStepUpWithAmr(assurance, amr)) {
|
|
518
|
+
throw new Error(`AMR ${amr} is already present on the session`);
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
...assurance,
|
|
522
|
+
amr: [...assurance.amr, amr],
|
|
523
|
+
aal: maxAal(assurance.aal, AALs.Aal2),
|
|
524
|
+
assuredAt: now.toISOString()
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const SessionAssuranceRequiredCode = z.enum([
|
|
529
|
+
"session_assurance_required",
|
|
530
|
+
"application_session_assurance_not_met"
|
|
531
|
+
]);
|
|
532
|
+
const SessionAssurancePayloadAssurance = z.preprocess((value) => {
|
|
533
|
+
if (typeof value !== "object" || value === null) {
|
|
534
|
+
return value;
|
|
535
|
+
}
|
|
536
|
+
const assurance = value;
|
|
537
|
+
if (!(assurance.assuredAt instanceof Date)) {
|
|
538
|
+
return value;
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
...assurance,
|
|
542
|
+
assuredAt: assurance.assuredAt.toISOString()
|
|
543
|
+
};
|
|
544
|
+
}, JWTPayloadAssurance);
|
|
545
|
+
const SessionAssuranceRequiredPayload = z.object({
|
|
546
|
+
code: SessionAssuranceRequiredCode,
|
|
547
|
+
currentAssurance: SessionAssurancePayloadAssurance,
|
|
548
|
+
requiredAssurance: SessionAssurancePolicy,
|
|
549
|
+
validStepUps: StepUpOption.array()
|
|
550
|
+
});
|
|
551
|
+
function getCandidatePayloads(error) {
|
|
552
|
+
const candidate = error;
|
|
553
|
+
return [
|
|
554
|
+
candidate.details,
|
|
555
|
+
candidate.error?.details,
|
|
556
|
+
candidate.data?.details,
|
|
557
|
+
candidate.body?.details,
|
|
558
|
+
candidate.payload,
|
|
559
|
+
error
|
|
560
|
+
].filter((payload) => payload !== void 0);
|
|
561
|
+
}
|
|
562
|
+
function toSdkCode(code) {
|
|
563
|
+
return code.toUpperCase();
|
|
564
|
+
}
|
|
565
|
+
function parseSessionAssuranceRequiredPayload(error) {
|
|
566
|
+
const seen = /* @__PURE__ */ new Set();
|
|
567
|
+
let current = error;
|
|
568
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
569
|
+
seen.add(current);
|
|
570
|
+
for (const payload of getCandidatePayloads(current)) {
|
|
571
|
+
const result = SessionAssuranceRequiredPayload.safeParse(payload);
|
|
572
|
+
if (result.success) {
|
|
573
|
+
return result.data;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
current = current.cause;
|
|
577
|
+
}
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
class SessionAssuranceRequiredError extends BaseError {
|
|
581
|
+
payload;
|
|
582
|
+
currentAssurance;
|
|
583
|
+
requiredAssurance;
|
|
584
|
+
validStepUps;
|
|
585
|
+
constructor(payload, options) {
|
|
586
|
+
super(toSdkCode(payload.code), "Session assurance is required for this action", options);
|
|
587
|
+
this.name = "SessionAssuranceRequiredError";
|
|
588
|
+
this.payload = payload;
|
|
589
|
+
this.currentAssurance = payload.currentAssurance;
|
|
590
|
+
this.requiredAssurance = payload.requiredAssurance;
|
|
591
|
+
this.validStepUps = payload.validStepUps;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const DEFAULT_SESSION_ASSURANCE_CONFIG = {
|
|
596
|
+
acceptedAmr: [
|
|
597
|
+
AMRs.Pwd,
|
|
598
|
+
AMRs.Oauth,
|
|
599
|
+
AMRs.Saml
|
|
600
|
+
],
|
|
601
|
+
minAal: AALs.Aal1
|
|
602
|
+
};
|
|
603
|
+
const DEFAULT_APPLICATION_CONFIG = {
|
|
604
|
+
version: 1,
|
|
605
|
+
sessionAssurance: DEFAULT_SESSION_ASSURANCE_CONFIG
|
|
606
|
+
};
|
|
607
|
+
const ApplicationSessionAssuranceConfig = z.object({
|
|
608
|
+
acceptedAmr: AMR.array().optional().default(DEFAULT_SESSION_ASSURANCE_CONFIG.acceptedAmr),
|
|
609
|
+
minAal: AAL.refine(
|
|
610
|
+
(aal) => aal !== AALs.Aal3,
|
|
611
|
+
"Application session assurance currently supports aal1 and aal2 policies"
|
|
612
|
+
).optional().default(DEFAULT_SESSION_ASSURANCE_CONFIG.minAal)
|
|
613
|
+
});
|
|
614
|
+
const ApplicationConfig = z.object({
|
|
615
|
+
version: z.literal(1),
|
|
616
|
+
sessionAssurance: ApplicationSessionAssuranceConfig.optional().default(DEFAULT_SESSION_ASSURANCE_CONFIG)
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
function allowsDefaultAmrPolicy(config) {
|
|
620
|
+
const acceptedAmr = new Set(config.sessionAssurance.acceptedAmr);
|
|
621
|
+
const defaultAcceptedAmr = DEFAULT_SESSION_ASSURANCE_CONFIG.acceptedAmr;
|
|
622
|
+
return config.sessionAssurance.minAal === DEFAULT_SESSION_ASSURANCE_CONFIG.minAal && defaultAcceptedAmr.every((amr) => acceptedAmr.has(amr));
|
|
623
|
+
}
|
|
624
|
+
function isSessionAssuranceSufficient(sessionAssurance, config = DEFAULT_APPLICATION_CONFIG) {
|
|
625
|
+
if (!sessionAssurance) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
if (!sessionAssurance.amr.length && allowsDefaultAmrPolicy(config)) {
|
|
629
|
+
return getAalRank(sessionAssurance.aal) >= getAalRank(config.sessionAssurance.minAal);
|
|
630
|
+
}
|
|
631
|
+
if (!isAssuranceSufficientForPolicy(sessionAssurance, config.sessionAssurance)) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
348
637
|
function parseErrorCode(error) {
|
|
349
638
|
if (!error || typeof error !== "object") {
|
|
350
639
|
return null;
|
|
@@ -376,6 +665,9 @@ function throwDeviceGrantError(error) {
|
|
|
376
665
|
if (code === "temporarily_unavailable") {
|
|
377
666
|
throw new DeviceTransientServerError({ cause: error });
|
|
378
667
|
}
|
|
668
|
+
if (code === "application_session_assurance_not_met") {
|
|
669
|
+
throw new ApplicationSessionAssuranceError(parseSessionAssuranceRequiredPayload(error), { cause: error });
|
|
670
|
+
}
|
|
379
671
|
throw new ApplicationError(parseErrorMessage(error), { cause: error });
|
|
380
672
|
}
|
|
381
673
|
class ApplicationService {
|
|
@@ -425,11 +717,26 @@ class ApplicationService {
|
|
|
425
717
|
* @param organizationId - The organization ID to start the authorization flow for
|
|
426
718
|
*/
|
|
427
719
|
async startAuthorizationFlow(applicationId, redirectUri, codeChallenge, organizationId) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
720
|
+
try {
|
|
721
|
+
const response = await this.client.applications.startAuthorizationFlow(applicationId, redirectUri, codeChallenge, organizationId);
|
|
722
|
+
if (!response.data) {
|
|
723
|
+
const payload = parseSessionAssuranceRequiredPayload(response.error);
|
|
724
|
+
if (payload) {
|
|
725
|
+
throw new SessionAssuranceRequiredError(payload, { cause: response.error });
|
|
726
|
+
}
|
|
727
|
+
throw new Error("No data returned from the API", { cause: response.error });
|
|
728
|
+
}
|
|
729
|
+
return response.data;
|
|
730
|
+
} catch (error) {
|
|
731
|
+
if (error instanceof SessionAssuranceRequiredError) {
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
const payload = parseSessionAssuranceRequiredPayload(error);
|
|
735
|
+
if (payload) {
|
|
736
|
+
throw new SessionAssuranceRequiredError(payload, { cause: error });
|
|
737
|
+
}
|
|
738
|
+
throw error;
|
|
431
739
|
}
|
|
432
|
-
return response.data;
|
|
433
740
|
}
|
|
434
741
|
/**
|
|
435
742
|
* Starts a device authorization flow (RFC 8628).
|
|
@@ -505,6 +812,7 @@ class ApplicationService {
|
|
|
505
812
|
* @throws {DeviceAccessDeniedError} The user denied the request
|
|
506
813
|
* @throws {DeviceCodeExpiredError} The device code has expired
|
|
507
814
|
* @throws {DeviceTransientServerError} Transient server error — safe to retry
|
|
815
|
+
* @throws {ApplicationSessionAssuranceError} Assurance policy changed or is no longer met — start a new flow
|
|
508
816
|
*/
|
|
509
817
|
async exchangeDeviceCodeForTokens(deviceCode) {
|
|
510
818
|
try {
|
|
@@ -1103,22 +1411,123 @@ class ApiKeyService {
|
|
|
1103
1411
|
}
|
|
1104
1412
|
}
|
|
1105
1413
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1414
|
+
function getApplicationId(options) {
|
|
1415
|
+
if (!options?.applicationId) {
|
|
1416
|
+
throw new Error("applicationId is required");
|
|
1417
|
+
}
|
|
1418
|
+
return options.applicationId;
|
|
1419
|
+
}
|
|
1420
|
+
class EmailOTPService {
|
|
1421
|
+
/**
|
|
1422
|
+
* Creates a new EmailOTPService instance.
|
|
1423
|
+
*
|
|
1424
|
+
* @param client - The API client for making email OTP requests
|
|
1425
|
+
*/
|
|
1426
|
+
constructor(client) {
|
|
1427
|
+
this.client = client;
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Sends a sign-in verification OTP to an email address.
|
|
1431
|
+
*
|
|
1432
|
+
* @param email - Email address that should receive the OTP
|
|
1433
|
+
* @param options - Request configuration
|
|
1434
|
+
* @returns The underlying email OTP API response
|
|
1435
|
+
*/
|
|
1436
|
+
async sendVerificationOTP(email, options) {
|
|
1437
|
+
const applicationId = getApplicationId(options);
|
|
1438
|
+
return await this.client.emailOtp.sendVerificationOtp({ email, type: "sign-in" }, { query: { applicationId } });
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Completes email OTP sign-in with a one-time passcode.
|
|
1442
|
+
*
|
|
1443
|
+
* @param email - Email address that received the OTP
|
|
1444
|
+
* @param otp - One-time passcode from the sign-in email
|
|
1445
|
+
* @param options - Request configuration
|
|
1446
|
+
* @returns The underlying email OTP sign-in API response
|
|
1447
|
+
*/
|
|
1448
|
+
async signIn(email, otp, options) {
|
|
1449
|
+
const applicationId = getApplicationId(options);
|
|
1450
|
+
return await this.client.signIn.emailOtp({ email, otp }, { query: { applicationId } });
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const ClientJWTPayloadAssurance = JWTPayloadAssurance.extend({
|
|
1455
|
+
assuredAt: z.date()
|
|
1456
|
+
});
|
|
1457
|
+
const SessionAssuranceResponse = z.object({
|
|
1458
|
+
assurance: ClientJWTPayloadAssurance,
|
|
1459
|
+
validStepUps: StepUpOption.array()
|
|
1460
|
+
});
|
|
1461
|
+
const OAuthBeginStepUpResponse = z.object({
|
|
1462
|
+
stepUpToken: z.string(),
|
|
1463
|
+
callbackURL: z.string()
|
|
1464
|
+
});
|
|
1465
|
+
const OAuthCompleteStepUpResponse = SessionAssuranceResponse.extend({
|
|
1466
|
+
returnPath: z.string()
|
|
1467
|
+
});
|
|
1468
|
+
const SendOtpResponse = z.object({
|
|
1469
|
+
success: z.boolean()
|
|
1470
|
+
});
|
|
1471
|
+
function isBetterAuthResponse(response) {
|
|
1472
|
+
return typeof response === "object" && response !== null && "data" in response && "error" in response;
|
|
1473
|
+
}
|
|
1474
|
+
function getResponseData(response) {
|
|
1475
|
+
if (!isBetterAuthResponse(response)) {
|
|
1476
|
+
return response;
|
|
1477
|
+
}
|
|
1478
|
+
if (response.data !== null && response.data !== void 0) {
|
|
1479
|
+
return response.data;
|
|
1480
|
+
}
|
|
1481
|
+
throw new Error("No data returned from the API", { cause: response.error });
|
|
1482
|
+
}
|
|
1483
|
+
function parseResponse(schema, response) {
|
|
1484
|
+
return schema.parse(getResponseData(response));
|
|
1485
|
+
}
|
|
1486
|
+
class SessionAssuranceService {
|
|
1487
|
+
constructor(client) {
|
|
1488
|
+
this.client = client;
|
|
1489
|
+
}
|
|
1490
|
+
async get() {
|
|
1491
|
+
return parseResponse(SessionAssuranceResponse, await this.client.sessionAssurance.getSessionAssurance());
|
|
1492
|
+
}
|
|
1493
|
+
async stepUpWithPassword(password) {
|
|
1494
|
+
return parseResponse(SessionAssuranceResponse, await this.client.sessionAssurance.stepUpWithPassword({ password }));
|
|
1495
|
+
}
|
|
1496
|
+
async sendOtp() {
|
|
1497
|
+
return parseResponse(SendOtpResponse, await this.client.sessionAssurance.sendOtp());
|
|
1498
|
+
}
|
|
1499
|
+
async verifyOtp(otp) {
|
|
1500
|
+
return parseResponse(SessionAssuranceResponse, await this.client.sessionAssurance.verifyOtp({ otp }));
|
|
1501
|
+
}
|
|
1502
|
+
async beginOAuthStepUp(params) {
|
|
1503
|
+
return parseResponse(OAuthBeginStepUpResponse, await this.client.sessionAssurance.beginOAuthStepUp(params));
|
|
1504
|
+
}
|
|
1505
|
+
async completeOAuthStepUp(stepUpToken) {
|
|
1506
|
+
return parseResponse(OAuthCompleteStepUpResponse, await this.client.sessionAssurance.completeOAuthStepUp({ stepUpToken }));
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const ApiKeyMetadata = z.object({
|
|
1511
|
+
user: z.object({
|
|
1512
|
+
id: z.string(),
|
|
1513
|
+
email: z.string()
|
|
1110
1514
|
}),
|
|
1111
|
-
workspace: z
|
|
1112
|
-
id: z
|
|
1113
|
-
title: z
|
|
1515
|
+
workspace: z.object({
|
|
1516
|
+
id: z.string(),
|
|
1517
|
+
title: z.string()
|
|
1114
1518
|
}),
|
|
1115
|
-
application: z
|
|
1116
|
-
id: z
|
|
1117
|
-
name: z
|
|
1519
|
+
application: z.object({
|
|
1520
|
+
id: z.string(),
|
|
1521
|
+
name: z.string()
|
|
1118
1522
|
}).optional().nullable()
|
|
1119
1523
|
});
|
|
1120
1524
|
|
|
1121
1525
|
class AuthClient {
|
|
1526
|
+
/**
|
|
1527
|
+
* Configured Better Auth client used by the public service wrappers.
|
|
1528
|
+
*
|
|
1529
|
+
* @internal
|
|
1530
|
+
*/
|
|
1122
1531
|
client;
|
|
1123
1532
|
/**
|
|
1124
1533
|
* Session management service for authentication operations
|
|
@@ -1136,6 +1545,14 @@ class AuthClient {
|
|
|
1136
1545
|
* API key management service for API key operations
|
|
1137
1546
|
*/
|
|
1138
1547
|
apiKey;
|
|
1548
|
+
/**
|
|
1549
|
+
* Email OTP service for passwordless sign-in operations
|
|
1550
|
+
*/
|
|
1551
|
+
emailOtp;
|
|
1552
|
+
/**
|
|
1553
|
+
* Session assurance service for step-up operations
|
|
1554
|
+
*/
|
|
1555
|
+
sessionAssurance;
|
|
1139
1556
|
/**
|
|
1140
1557
|
* Creates a new AuthClient instance.
|
|
1141
1558
|
*
|
|
@@ -1148,6 +1565,8 @@ class AuthClient {
|
|
|
1148
1565
|
this.organization = new OrganizationService(this.client);
|
|
1149
1566
|
this.application = new ApplicationService(this.client);
|
|
1150
1567
|
this.apiKey = new ApiKeyService(this.client);
|
|
1568
|
+
this.emailOtp = new EmailOTPService(this.client);
|
|
1569
|
+
this.sessionAssurance = new SessionAssuranceService(this.client);
|
|
1151
1570
|
}
|
|
1152
1571
|
}
|
|
1153
1572
|
|
|
@@ -1184,4 +1603,4 @@ function extractTokenPayload(token) {
|
|
|
1184
1603
|
return payload;
|
|
1185
1604
|
}
|
|
1186
1605
|
|
|
1187
|
-
export { ApiKeyMetadata, ApplicationError, AuthClient, AuthorizationFlowError, DeviceAccessDeniedError, DeviceAuthorizationPendingError, DeviceAuthorizationSlowDownError, DeviceCodeExpiredError, DeviceTransientServerError, EmailRequired, InvalidCallbackURL, InvalidSocialProvider, JWTPayload, JWTPayloadUser, JWTPayloadWorkspace, RefreshTokenExpiredError, Roles, UserNotLoggedInError, ac, extractTokenPayload, invitationAdditionalFields, isTokenExpired, memberAdditionalFields, organizationAdditionalFields, rolesAccessControl, userAdditionalFields, validateToken };
|
|
1606
|
+
export { AAL, AALBrand, AALs, ACR, ACRBrand, ACRs, AMR, AMRBrand, AMRs, ApiKeyMetadata, ApplicationConfig, ApplicationError, ApplicationSessionAssuranceConfig, ApplicationSessionAssuranceError, AuthClient, AuthMethod, AuthMethodBrand, AuthMethods, AuthorizationFlowError, DEFAULT_APPLICATION_CONFIG, DEFAULT_JWT_ASSURANCE, DEFAULT_SESSION_ASSURANCE_CONFIG, DeviceAccessDeniedError, DeviceAuthorizationPendingError, DeviceAuthorizationSlowDownError, DeviceCodeExpiredError, DeviceTransientServerError, EmailOTPService, EmailRequired, InvalidCallbackURL, InvalidSocialProvider, JWTPayload, JWTPayloadAssurance, JWTPayloadUser, JWTPayloadWorkspace, OAuthStepUpProvider, RefreshTokenExpiredError, Roles, SENSITIVE_ACTION_ASSURANCE_POLICY, SessionAssurancePolicy, SessionAssuranceRequiredError, SessionAssuranceRequiredPayload, SessionAssuranceService, StepUpOption, UserNotLoggedInError, ac, assuranceFields, canStepUpWithAmr, extractTokenPayload, getAalRank, invitationAdditionalFields, isAssuranceSufficientForPolicy, isSessionAssuranceSufficient, isTokenExpired, memberAdditionalFields, mergeStepUpAssurance, organizationAdditionalFields, parseSessionAssuranceRequiredPayload, rolesAccessControl, sessionAssurancePluginClient, userAdditionalFields, validateToken };
|