better-auth 1.6.16 → 1.6.17
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/api/index.d.mts +2 -2
- package/dist/api/index.mjs +3 -4
- package/dist/api/middlewares/origin-check.mjs +5 -1
- package/dist/api/rate-limiter/index.mjs +259 -73
- package/dist/api/routes/account.mjs +22 -7
- package/dist/api/routes/callback.mjs +2 -2
- package/dist/api/routes/index.d.mts +1 -1
- package/dist/api/routes/password.mjs +3 -4
- package/dist/api/routes/session.d.mts +12 -1
- package/dist/api/routes/session.mjs +13 -1
- package/dist/api/routes/sign-in.mjs +5 -5
- package/dist/api/routes/sign-up.mjs +2 -2
- package/dist/api/routes/update-session.mjs +2 -3
- package/dist/api/routes/update-user.mjs +10 -12
- package/dist/auth/base.mjs +11 -7
- package/dist/client/equality.d.mts +19 -0
- package/dist/client/equality.mjs +42 -0
- package/dist/client/index.d.mts +5 -4
- package/dist/client/index.mjs +2 -1
- package/dist/client/path-to-object.d.mts +5 -2
- package/dist/client/plugins/index.d.mts +4 -1
- package/dist/client/plugins/index.mjs +4 -1
- package/dist/client/query.d.mts +4 -3
- package/dist/client/query.mjs +27 -17
- package/dist/client/session-atom.mjs +129 -4
- package/dist/client/session-refresh.d.mts +3 -18
- package/dist/client/session-refresh.mjs +38 -49
- package/dist/client/types.d.mts +2 -2
- package/dist/context/create-context.mjs +2 -1
- package/dist/context/store-capabilities.mjs +12 -0
- package/dist/cookies/index.mjs +25 -2
- package/dist/db/internal-adapter.mjs +51 -0
- package/dist/package.mjs +1 -1
- package/dist/plugins/access/access.mjs +49 -19
- package/dist/plugins/admin/routes.mjs +10 -3
- package/dist/plugins/captcha/constants.mjs +8 -1
- package/dist/plugins/captcha/index.mjs +8 -2
- package/dist/plugins/captcha/types.d.mts +21 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -0
- package/dist/plugins/device-authorization/routes.mjs +16 -9
- package/dist/plugins/email-otp/routes.mjs +22 -52
- package/dist/plugins/generic-oauth/index.mjs +7 -2
- package/dist/plugins/generic-oauth/routes.mjs +16 -12
- package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
- package/dist/plugins/haveibeenpwned/index.mjs +5 -1
- package/dist/plugins/index.d.mts +5 -1
- package/dist/plugins/index.mjs +4 -1
- package/dist/plugins/jwt/index.mjs +2 -2
- package/dist/plugins/mcp/client/index.mjs +1 -0
- package/dist/plugins/mcp/index.mjs +8 -0
- package/dist/plugins/multi-session/index.mjs +7 -5
- package/dist/plugins/oauth-popup/client.d.mts +82 -0
- package/dist/plugins/oauth-popup/client.mjs +203 -0
- package/dist/plugins/oauth-popup/constants.d.mts +11 -0
- package/dist/plugins/oauth-popup/constants.mjs +11 -0
- package/dist/plugins/oauth-popup/error-codes.d.mts +11 -0
- package/dist/plugins/oauth-popup/error-codes.mjs +10 -0
- package/dist/plugins/oauth-popup/index.d.mts +67 -0
- package/dist/plugins/oauth-popup/index.mjs +227 -0
- package/dist/plugins/oauth-popup/types.d.mts +30 -0
- package/dist/plugins/oauth-proxy/index.mjs +2 -2
- package/dist/plugins/oauth-proxy/utils.mjs +16 -2
- package/dist/plugins/oidc-provider/index.mjs +10 -0
- package/dist/plugins/one-tap/client.mjs +12 -6
- package/dist/plugins/one-tap/index.d.mts +1 -0
- package/dist/plugins/one-tap/index.mjs +9 -5
- package/dist/plugins/one-time-token/index.mjs +1 -3
- package/dist/plugins/open-api/generator.mjs +7 -4
- package/dist/plugins/organization/adapter.d.mts +29 -1
- package/dist/plugins/organization/adapter.mjs +66 -6
- package/dist/plugins/organization/routes/crud-invites.mjs +49 -34
- package/dist/plugins/organization/routes/crud-members.mjs +42 -6
- package/dist/plugins/organization/routes/crud-team.mjs +36 -3
- package/dist/plugins/phone-number/routes.mjs +41 -36
- package/dist/plugins/siwe/index.mjs +2 -3
- package/dist/plugins/two-factor/backup-codes/index.mjs +1 -1
- package/dist/plugins/two-factor/otp/index.mjs +11 -13
- package/dist/plugins/two-factor/totp/index.mjs +1 -1
- package/dist/plugins/two-factor/verify-two-factor.mjs +6 -2
- package/dist/plugins/username/index.mjs +6 -6
- package/package.json +9 -9
|
@@ -243,7 +243,21 @@ Follow [rfc8628#section-3.4](https://datatracker.ietf.org/doc/html/rfc8628#secti
|
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
if (deviceCodeRecord.status === "approved" && deviceCodeRecord.userId) {
|
|
246
|
-
const
|
|
246
|
+
const claimedDeviceCode = await ctx.context.adapter.consumeOne({
|
|
247
|
+
model: "deviceCode",
|
|
248
|
+
where: [{
|
|
249
|
+
field: "deviceCode",
|
|
250
|
+
value: device_code
|
|
251
|
+
}, {
|
|
252
|
+
field: "status",
|
|
253
|
+
value: "approved"
|
|
254
|
+
}]
|
|
255
|
+
});
|
|
256
|
+
if (!claimedDeviceCode?.userId) throw new APIError("BAD_REQUEST", {
|
|
257
|
+
error: "invalid_grant",
|
|
258
|
+
error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_DEVICE_CODE.message
|
|
259
|
+
});
|
|
260
|
+
const user = await ctx.context.internalAdapter.findUserById(claimedDeviceCode.userId);
|
|
247
261
|
if (!user) throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
248
262
|
error: "server_error",
|
|
249
263
|
error_description: DEVICE_AUTHORIZATION_ERROR_CODES.USER_NOT_FOUND.message
|
|
@@ -261,18 +275,11 @@ Follow [rfc8628#section-3.4](https://datatracker.ietf.org/doc/html/rfc8628#secti
|
|
|
261
275
|
user,
|
|
262
276
|
session
|
|
263
277
|
}), Math.floor((new Date(session.expiresAt).getTime() - Date.now()) / 1e3));
|
|
264
|
-
await ctx.context.adapter.delete({
|
|
265
|
-
model: "deviceCode",
|
|
266
|
-
where: [{
|
|
267
|
-
field: "id",
|
|
268
|
-
value: deviceCodeRecord.id
|
|
269
|
-
}]
|
|
270
|
-
});
|
|
271
278
|
return ctx.json({
|
|
272
279
|
access_token: session.token,
|
|
273
280
|
token_type: "Bearer",
|
|
274
281
|
expires_in: Math.floor((new Date(session.expiresAt).getTime() - Date.now()) / 1e3),
|
|
275
|
-
scope:
|
|
282
|
+
scope: claimedDeviceCode.scope || ""
|
|
276
283
|
}, { headers: {
|
|
277
284
|
"Cache-Control": "no-store",
|
|
278
285
|
Pragma: "no-cache"
|
|
@@ -115,7 +115,7 @@ const createVerificationOTPBodySchema = z.object({
|
|
|
115
115
|
description: "Type of the OTP"
|
|
116
116
|
})
|
|
117
117
|
});
|
|
118
|
-
const createVerificationOTP = (opts) => createAuthEndpoint({
|
|
118
|
+
const createVerificationOTP = (opts) => createAuthEndpoint.serverOnly({
|
|
119
119
|
method: "POST",
|
|
120
120
|
body: createVerificationOTPBodySchema,
|
|
121
121
|
metadata: { openapi: {
|
|
@@ -159,7 +159,7 @@ const getVerificationOTPBodySchema = z.object({
|
|
|
159
159
|
*
|
|
160
160
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/email-otp#api-method-email-otp-get-verification-otp)
|
|
161
161
|
*/
|
|
162
|
-
const getVerificationOTP = (opts) => createAuthEndpoint({
|
|
162
|
+
const getVerificationOTP = (opts) => createAuthEndpoint.serverOnly({
|
|
163
163
|
method: "GET",
|
|
164
164
|
query: getVerificationOTPBodySchema,
|
|
165
165
|
metadata: { openapi: {
|
|
@@ -636,24 +636,7 @@ const requestEmailChangeEmailOTP = (opts) => createAuthEndpoint("/email-otp/requ
|
|
|
636
636
|
}
|
|
637
637
|
if (opts.changeEmail?.verifyCurrentEmail) {
|
|
638
638
|
if (!ctx.body.otp) throw APIError$1.fromStatus("BAD_REQUEST", { message: "OTP is required to verify current email" });
|
|
639
|
-
|
|
640
|
-
if (!currentEmailVerificationValue) throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
641
|
-
const currentEmailIdentifier = toOTPIdentifier("email-verification", email);
|
|
642
|
-
if (currentEmailVerificationValue.expiresAt < /* @__PURE__ */ new Date()) {
|
|
643
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(currentEmailIdentifier);
|
|
644
|
-
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.OTP_EXPIRED);
|
|
645
|
-
}
|
|
646
|
-
const [otpValue, attempts] = splitAtLastColon(currentEmailVerificationValue.value);
|
|
647
|
-
const allowedAttempts = opts?.allowedAttempts || 3;
|
|
648
|
-
if (attempts && parseInt(attempts) >= allowedAttempts) {
|
|
649
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(currentEmailIdentifier);
|
|
650
|
-
throw APIError$1.from("FORBIDDEN", EMAIL_OTP_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
651
|
-
}
|
|
652
|
-
if (!await verifyStoredOTP(ctx, opts, otpValue, ctx.body.otp)) {
|
|
653
|
-
await ctx.context.internalAdapter.updateVerificationByIdentifier(currentEmailIdentifier, { value: `${otpValue}:${parseInt(attempts || "0") + 1}` });
|
|
654
|
-
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
655
|
-
}
|
|
656
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(currentEmailIdentifier);
|
|
639
|
+
await atomicVerifyOTP(ctx, opts, toOTPIdentifier("email-verification", email), ctx.body.otp);
|
|
657
640
|
} else if (ctx.body.otp) ctx.context.logger.warn("OTP provided but not required for verifying current email. If you want to require OTP verification for current email, please set the changeEmail.verifyCurrentEmail option to true in the configuration");
|
|
658
641
|
const otp = opts.generateOTP({
|
|
659
642
|
email: newEmail,
|
|
@@ -723,24 +706,7 @@ const changeEmailEmailOTP = (opts) => createAuthEndpoint("/email-otp/change-emai
|
|
|
723
706
|
ctx.context.logger.error("Email is the same");
|
|
724
707
|
throw APIError$1.fromStatus("BAD_REQUEST", { message: "Email is the same" });
|
|
725
708
|
}
|
|
726
|
-
|
|
727
|
-
if (!verificationValue) throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
728
|
-
const changeEmailIdentifier = toOTPIdentifier("change-email", `${email}-${newEmail}`);
|
|
729
|
-
if (verificationValue.expiresAt < /* @__PURE__ */ new Date()) {
|
|
730
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(changeEmailIdentifier);
|
|
731
|
-
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.OTP_EXPIRED);
|
|
732
|
-
}
|
|
733
|
-
const [otpValue, attempts] = splitAtLastColon(verificationValue.value);
|
|
734
|
-
const allowedAttempts = opts?.allowedAttempts || 3;
|
|
735
|
-
if (attempts && parseInt(attempts) >= allowedAttempts) {
|
|
736
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(changeEmailIdentifier);
|
|
737
|
-
throw APIError$1.from("FORBIDDEN", EMAIL_OTP_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
738
|
-
}
|
|
739
|
-
if (!await verifyStoredOTP(ctx, opts, otpValue, ctx.body.otp)) {
|
|
740
|
-
await ctx.context.internalAdapter.updateVerificationByIdentifier(changeEmailIdentifier, { value: `${otpValue}:${parseInt(attempts || "0") + 1}` });
|
|
741
|
-
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
742
|
-
}
|
|
743
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(changeEmailIdentifier);
|
|
709
|
+
await atomicVerifyOTP(ctx, opts, toOTPIdentifier("change-email", `${email}-${newEmail}`), ctx.body.otp);
|
|
744
710
|
const currentUser = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
745
711
|
if (!currentUser)
|
|
746
712
|
/**
|
|
@@ -770,29 +736,33 @@ const changeEmailEmailOTP = (opts) => createAuthEndpoint("/email-otp/change-emai
|
|
|
770
736
|
});
|
|
771
737
|
const defaultOTPGenerator = (options) => generateRandomString(options.otpLength ?? 6, "0-9");
|
|
772
738
|
/**
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
739
|
+
* Verifies a single-use OTP with race-condition protection.
|
|
740
|
+
*
|
|
741
|
+
* The atomic consume is the single gate: only the first concurrent caller
|
|
742
|
+
* receives the record, every later racer receives `null` and is rejected, so
|
|
743
|
+
* a correct OTP can only ever be accepted once. When the submitted code is
|
|
744
|
+
* wrong the record is recreated with the same value and expiry and an
|
|
745
|
+
* incremented attempt count so the next try can still find it; the budget is
|
|
746
|
+
* enforced before verification, and a record whose attempts are exhausted is
|
|
747
|
+
* left consumed (no recreate), locking the identifier out.
|
|
776
748
|
*/
|
|
777
749
|
async function atomicVerifyOTP(ctx, opts, identifier, providedOTP) {
|
|
778
|
-
const
|
|
779
|
-
if (
|
|
780
|
-
if (verificationValue.expiresAt < /* @__PURE__ */ new Date()) {
|
|
750
|
+
const existing = await ctx.context.internalAdapter.findVerificationValue(identifier);
|
|
751
|
+
if (existing && existing.expiresAt < /* @__PURE__ */ new Date()) {
|
|
781
752
|
await ctx.context.internalAdapter.deleteVerificationByIdentifier(identifier);
|
|
782
753
|
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.OTP_EXPIRED);
|
|
783
754
|
}
|
|
784
|
-
const
|
|
755
|
+
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(identifier);
|
|
756
|
+
if (!consumed) throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
757
|
+
const [otpValue, attempts] = splitAtLastColon(consumed.value);
|
|
785
758
|
const allowedAttempts = opts?.allowedAttempts || 3;
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
throw APIError$1.from("FORBIDDEN", EMAIL_OTP_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
789
|
-
}
|
|
790
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(identifier);
|
|
759
|
+
const usedAttempts = parseInt(attempts || "0");
|
|
760
|
+
if (usedAttempts >= allowedAttempts) throw APIError$1.from("FORBIDDEN", EMAIL_OTP_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
791
761
|
if (!await verifyStoredOTP(ctx, opts, otpValue, providedOTP)) {
|
|
792
762
|
await ctx.context.internalAdapter.createVerificationValue({
|
|
793
|
-
value: `${otpValue}:${
|
|
763
|
+
value: `${otpValue}:${usedAttempts + 1}`,
|
|
794
764
|
identifier,
|
|
795
|
-
expiresAt:
|
|
765
|
+
expiresAt: consumed.expiresAt
|
|
796
766
|
});
|
|
797
767
|
throw APIError$1.from("BAD_REQUEST", EMAIL_OTP_ERROR_CODES.INVALID_OTP);
|
|
798
768
|
}
|
|
@@ -14,6 +14,9 @@ import { APIError } from "@better-auth/core/error";
|
|
|
14
14
|
import { applyDefaultAccessTokenExpiry, createAuthorizationURL, refreshAccessToken, validateAuthorizationCode } from "@better-auth/core/oauth2";
|
|
15
15
|
import { betterFetch } from "@better-fetch/fetch";
|
|
16
16
|
//#region src/plugins/generic-oauth/index.ts
|
|
17
|
+
function isNonEmptyOAuthId(id) {
|
|
18
|
+
return id !== void 0 && id !== null && id !== "";
|
|
19
|
+
}
|
|
17
20
|
/**
|
|
18
21
|
* A generic OAuth plugin that can be used to add OAuth support to any provider
|
|
19
22
|
*/
|
|
@@ -114,14 +117,16 @@ const genericOAuth = (options) => {
|
|
|
114
117
|
const userInfo = c.getUserInfo ? await c.getUserInfo(tokens) : await getUserInfo(tokens, finalUserInfoUrl);
|
|
115
118
|
if (!userInfo) return null;
|
|
116
119
|
const userMap = await c.mapProfileToUser?.(userInfo);
|
|
120
|
+
const rawId = isNonEmptyOAuthId(userMap?.id) ? userMap.id : isNonEmptyOAuthId(userInfo.id) ? userInfo.id : isNonEmptyOAuthId(userInfo.sub) ? userInfo.sub : void 0;
|
|
121
|
+
if (rawId === void 0) return null;
|
|
117
122
|
return {
|
|
118
123
|
user: {
|
|
119
|
-
id: userInfo?.id,
|
|
120
124
|
email: userInfo?.email,
|
|
121
125
|
emailVerified: userInfo?.emailVerified,
|
|
122
126
|
image: userInfo?.image,
|
|
123
127
|
name: userInfo?.name,
|
|
124
|
-
...userMap
|
|
128
|
+
...userMap,
|
|
129
|
+
id: String(rawId)
|
|
125
130
|
},
|
|
126
131
|
data: userInfo
|
|
127
132
|
};
|
|
@@ -15,6 +15,9 @@ import * as z from "zod";
|
|
|
15
15
|
import { decodeJwt } from "jose";
|
|
16
16
|
import { betterFetch } from "@better-fetch/fetch";
|
|
17
17
|
//#region src/plugins/generic-oauth/routes.ts
|
|
18
|
+
function isNonEmptyOAuthId(id) {
|
|
19
|
+
return id !== void 0 && id !== null && id !== "";
|
|
20
|
+
}
|
|
18
21
|
const signInWithOAuth2BodySchema = z.object({
|
|
19
22
|
providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }),
|
|
20
23
|
callbackURL: z.string().meta({ description: "The URL to redirect to after sign in" }).optional(),
|
|
@@ -209,8 +212,8 @@ const oAuth2Callback = (options) => createAuthEndpoint("/oauth2/callback/:provid
|
|
|
209
212
|
ctx.context.logger.error(missingEmailLogMessage(providerConfig.providerId, { source: "generic" }), userInfo);
|
|
210
213
|
redirectOnError(ctx, resolvedErrorURL, "email_is_missing");
|
|
211
214
|
}
|
|
212
|
-
const rawId = mapUser.id
|
|
213
|
-
const id = rawId !== void 0
|
|
215
|
+
const rawId = isNonEmptyOAuthId(mapUser.id) ? mapUser.id : isNonEmptyOAuthId(userInfo.id) ? userInfo.id : isNonEmptyOAuthId(userInfo.sub) ? userInfo.sub : void 0;
|
|
216
|
+
const id = rawId !== void 0 ? String(rawId) : "";
|
|
214
217
|
if (!id) {
|
|
215
218
|
ctx.context.logger.error("Provider did not return an account id (e.g. `sub`). Unable to sign in.", userInfo);
|
|
216
219
|
redirectOnError(ctx, resolvedErrorURL, "id_is_missing");
|
|
@@ -399,19 +402,20 @@ async function getUserInfo(tokens, finalUserInfoUrl) {
|
|
|
399
402
|
}
|
|
400
403
|
}
|
|
401
404
|
if (!finalUserInfoUrl) return null;
|
|
402
|
-
const
|
|
405
|
+
const profile = (await betterFetch(finalUserInfoUrl, {
|
|
403
406
|
method: "GET",
|
|
404
407
|
headers: { Authorization: `Bearer ${tokens.accessToken}` }
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
|
|
408
|
+
})).data;
|
|
409
|
+
if (!profile) return null;
|
|
410
|
+
const { id: profileId, ...profileFields } = profile;
|
|
411
|
+
const subjectId = isNonEmptyOAuthId(profileId) ? profileId : isNonEmptyOAuthId(profile.sub) ? profile.sub : void 0;
|
|
408
412
|
return {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
email:
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
413
|
+
...profileFields,
|
|
414
|
+
...subjectId !== void 0 ? { id: subjectId } : {},
|
|
415
|
+
email: profile?.email,
|
|
416
|
+
emailVerified: profile?.email_verified ?? false,
|
|
417
|
+
image: profile?.picture,
|
|
418
|
+
name: profile?.name
|
|
415
419
|
};
|
|
416
420
|
}
|
|
417
421
|
//#endregion
|
|
@@ -17,7 +17,7 @@ interface HaveIBeenPwnedOptions {
|
|
|
17
17
|
/**
|
|
18
18
|
* Paths to check for password
|
|
19
19
|
*
|
|
20
|
-
* @default ["/sign-up/email", "/change-password", "/reset-password"]
|
|
20
|
+
* @default ["/sign-up/email", "/change-password", "/reset-password", "/email-otp/reset-password", "/phone-number/reset-password", "/admin/create-user", "/admin/set-user-password"]
|
|
21
21
|
*/
|
|
22
22
|
paths?: string[];
|
|
23
23
|
/**
|
|
@@ -31,7 +31,11 @@ const haveIBeenPwned = (options) => {
|
|
|
31
31
|
const paths = options?.paths || [
|
|
32
32
|
"/sign-up/email",
|
|
33
33
|
"/change-password",
|
|
34
|
-
"/reset-password"
|
|
34
|
+
"/reset-password",
|
|
35
|
+
"/email-otp/reset-password",
|
|
36
|
+
"/phone-number/reset-password",
|
|
37
|
+
"/admin/create-user",
|
|
38
|
+
"/admin/set-user-password"
|
|
35
39
|
];
|
|
36
40
|
return {
|
|
37
41
|
id: "have-i-been-pwned",
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -41,6 +41,10 @@ import { getClient, getMetadata, oidcProvider } from "./oidc-provider/index.mjs"
|
|
|
41
41
|
import { getMCPProtectedResourceMetadata, getMCPProviderMetadata, mcp, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, withMcpAuth } from "./mcp/index.mjs";
|
|
42
42
|
import { MULTI_SESSION_ERROR_CODES } from "./multi-session/error-codes.mjs";
|
|
43
43
|
import { MultiSessionConfig, multiSession } from "./multi-session/index.mjs";
|
|
44
|
+
import { OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_MESSAGE_TYPE, POPUP_MARKER_COOKIE } from "./oauth-popup/constants.mjs";
|
|
45
|
+
import { OAUTH_POPUP_ERROR_CODES } from "./oauth-popup/error-codes.mjs";
|
|
46
|
+
import { OAuthPopupData, OAuthPopupMessage } from "./oauth-popup/types.mjs";
|
|
47
|
+
import { OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_SCRIPT_CSP_HASH, oauthPopup } from "./oauth-popup/index.mjs";
|
|
44
48
|
import { OAuthProxyOptions, oAuthProxy } from "./oauth-proxy/index.mjs";
|
|
45
49
|
import { OneTapOptions, oneTap } from "./one-tap/index.mjs";
|
|
46
50
|
import { OneTimeTokenOptions, oneTimeToken } from "./one-time-token/index.mjs";
|
|
@@ -62,4 +66,4 @@ import { USERNAME_ERROR_CODES } from "./username/error-codes.mjs";
|
|
|
62
66
|
import { UsernameOptions, username } from "./username/index.mjs";
|
|
63
67
|
import { hasPermission } from "./organization/has-permission.mjs";
|
|
64
68
|
import { DefaultOrganizationPlugin, DynamicAccessControlEndpoints, OrganizationCreator, OrganizationEndpoints, OrganizationPlugin, TeamEndpoints, organization, parseRoles } from "./organization/organization.mjs";
|
|
65
|
-
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizationQuery, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, CaptchaFoxOptions, CaptchaOptions, Client, CloudflareTurnstileOptions, CodeVerificationValue, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationOptions, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, ExactRoleStatements, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OAuthProxyOptions, OIDCMetadata, OIDCOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, Path, PatreonOptions, PhoneNumberOptions, Provider, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, encodeBackupCodes, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, memberSchema, microsoftEntraId, ms, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, withMcpAuth };
|
|
69
|
+
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizationQuery, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, CaptchaFoxOptions, CaptchaOptions, Client, CloudflareTurnstileOptions, CodeVerificationValue, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationOptions, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, ExactRoleStatements, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_ERROR_CODES, OAUTH_POPUP_MESSAGE_TYPE, OAUTH_POPUP_SCRIPT_CSP_HASH, OAuthAccessToken, OAuthPopupData, OAuthPopupMessage, OAuthProxyOptions, OIDCMetadata, OIDCOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, POPUP_MARKER_COOKIE, Path, PatreonOptions, PhoneNumberOptions, Provider, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, encodeBackupCodes, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, memberSchema, microsoftEntraId, ms, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oauthPopup, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, withMcpAuth };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { HIDE_METADATA } from "../utils/hide-metadata.mjs";
|
|
2
2
|
import { createAccessControl, role } from "./access/access.mjs";
|
|
3
3
|
import { MULTI_SESSION_ERROR_CODES } from "./multi-session/error-codes.mjs";
|
|
4
|
+
import { OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_MESSAGE_TYPE, POPUP_MARKER_COOKIE } from "./oauth-popup/constants.mjs";
|
|
5
|
+
import { OAUTH_POPUP_ERROR_CODES } from "./oauth-popup/error-codes.mjs";
|
|
4
6
|
import { TWO_FACTOR_ERROR_CODES } from "./two-factor/error-code.mjs";
|
|
5
7
|
import { twoFactorClient } from "./two-factor/client.mjs";
|
|
6
8
|
import { USERNAME_ERROR_CODES } from "./username/error-codes.mjs";
|
|
@@ -31,6 +33,7 @@ import { magicLink } from "./magic-link/index.mjs";
|
|
|
31
33
|
import { getClient, getMetadata, oidcProvider } from "./oidc-provider/index.mjs";
|
|
32
34
|
import { getMCPProtectedResourceMetadata, getMCPProviderMetadata, mcp, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, withMcpAuth } from "./mcp/index.mjs";
|
|
33
35
|
import { multiSession } from "./multi-session/index.mjs";
|
|
36
|
+
import { OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_SCRIPT_CSP_HASH, oauthPopup } from "./oauth-popup/index.mjs";
|
|
34
37
|
import { oAuthProxy } from "./oauth-proxy/index.mjs";
|
|
35
38
|
import { oneTap } from "./one-tap/index.mjs";
|
|
36
39
|
import { oneTimeToken } from "./one-time-token/index.mjs";
|
|
@@ -43,4 +46,4 @@ import { siwe } from "./siwe/index.mjs";
|
|
|
43
46
|
import { testUtils } from "./test-utils/index.mjs";
|
|
44
47
|
import { twoFactor } from "./two-factor/index.mjs";
|
|
45
48
|
import { username } from "./username/index.mjs";
|
|
46
|
-
export { MULTI_SESSION_ERROR_CODES as ERROR_CODES, HIDE_METADATA, TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, admin, anonymous, auth0, bearer, captcha, createAccessControl, createJwk, customSession, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, generateExportedKeyPair, genericOAuth, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, microsoftEntraId, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, parseRoles, patreon, phoneNumber, role, signJWT, siwe, slack, testUtils, toExpJWT, twoFactor, twoFactorClient, username, verifyJWT, withMcpAuth };
|
|
49
|
+
export { MULTI_SESSION_ERROR_CODES as ERROR_CODES, HIDE_METADATA, OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_ERROR_CODES, OAUTH_POPUP_MESSAGE_TYPE, OAUTH_POPUP_SCRIPT_CSP_HASH, POPUP_MARKER_COOKIE, TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, admin, anonymous, auth0, bearer, captcha, createAccessControl, createJwk, customSession, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, generateExportedKeyPair, genericOAuth, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, microsoftEntraId, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oauthPopup, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, parseRoles, patreon, phoneNumber, role, signJWT, siwe, slack, testUtils, toExpJWT, twoFactor, twoFactorClient, username, verifyJWT, withMcpAuth };
|
|
@@ -144,7 +144,7 @@ const jwt = (options) => {
|
|
|
144
144
|
const jwt = await getJwtToken(ctx, options);
|
|
145
145
|
return ctx.json({ token: jwt });
|
|
146
146
|
}),
|
|
147
|
-
signJWT: createAuthEndpoint({
|
|
147
|
+
signJWT: createAuthEndpoint.serverOnly({
|
|
148
148
|
method: "POST",
|
|
149
149
|
metadata: { $Infer: { body: {} } },
|
|
150
150
|
body: signJWTBodySchema
|
|
@@ -158,7 +158,7 @@ const jwt = (options) => {
|
|
|
158
158
|
});
|
|
159
159
|
return c.json({ token: jwt });
|
|
160
160
|
}),
|
|
161
|
-
verifyJWT: createAuthEndpoint({
|
|
161
|
+
verifyJWT: createAuthEndpoint.serverOnly({
|
|
162
162
|
method: "POST",
|
|
163
163
|
metadata: { $Infer: {
|
|
164
164
|
body: {},
|
|
@@ -65,6 +65,7 @@ function createMcpAuthClient(options) {
|
|
|
65
65
|
if (!response.ok) return null;
|
|
66
66
|
const data = await response.json();
|
|
67
67
|
if (!data || !data.userId) return null;
|
|
68
|
+
if (data.accessTokenExpiresAt && new Date(data.accessTokenExpiresAt).getTime() < Date.now()) return null;
|
|
68
69
|
return data;
|
|
69
70
|
} catch {
|
|
70
71
|
return null;
|
|
@@ -302,6 +302,10 @@ const mcp = (options) => {
|
|
|
302
302
|
error_description: "refresh token expired",
|
|
303
303
|
error: "invalid_grant"
|
|
304
304
|
});
|
|
305
|
+
if (!token.scopes?.split(" ").includes("offline_access")) throw new APIError("UNAUTHORIZED", {
|
|
306
|
+
error_description: "refresh token was not issued for the offline_access scope",
|
|
307
|
+
error: "invalid_grant"
|
|
308
|
+
});
|
|
305
309
|
const refreshClient = await ctx.context.adapter.findOne({
|
|
306
310
|
model: modelName.oauthClient,
|
|
307
311
|
where: [{
|
|
@@ -693,6 +697,10 @@ const mcp = (options) => {
|
|
|
693
697
|
}]
|
|
694
698
|
});
|
|
695
699
|
if (!accessTokenData) return c.json(null);
|
|
700
|
+
if (accessTokenData.accessTokenExpiresAt < /* @__PURE__ */ new Date()) {
|
|
701
|
+
c.headers?.set("WWW-Authenticate", "Bearer");
|
|
702
|
+
return c.json(null);
|
|
703
|
+
}
|
|
696
704
|
return c.json(accessTokenData);
|
|
697
705
|
})
|
|
698
706
|
},
|
|
@@ -55,8 +55,9 @@ const multiSession = (options) => {
|
|
|
55
55
|
}, async (ctx) => {
|
|
56
56
|
const sessionToken = ctx.body.sessionToken;
|
|
57
57
|
const multiSessionCookieName = `${ctx.context.authCookies.sessionToken.name}_multi-${sessionToken.toLowerCase()}`;
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const sessionCookie = await ctx.getSignedCookie(multiSessionCookieName, ctx.context.secret);
|
|
59
|
+
if (!sessionCookie) throw APIError.from("UNAUTHORIZED", MULTI_SESSION_ERROR_CODES.INVALID_SESSION_TOKEN);
|
|
60
|
+
const session = await ctx.context.internalAdapter.findSession(sessionCookie);
|
|
60
61
|
if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) {
|
|
61
62
|
expireCookie(ctx, {
|
|
62
63
|
name: multiSessionCookieName,
|
|
@@ -88,13 +89,14 @@ const multiSession = (options) => {
|
|
|
88
89
|
}, async (ctx) => {
|
|
89
90
|
const sessionToken = ctx.body.sessionToken;
|
|
90
91
|
const multiSessionCookieName = `${ctx.context.authCookies.sessionToken.name}_multi-${sessionToken.toLowerCase()}`;
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
const sessionCookie = await ctx.getSignedCookie(multiSessionCookieName, ctx.context.secret);
|
|
93
|
+
if (!sessionCookie) throw APIError.from("UNAUTHORIZED", MULTI_SESSION_ERROR_CODES.INVALID_SESSION_TOKEN);
|
|
94
|
+
await ctx.context.internalAdapter.deleteSession(sessionCookie);
|
|
93
95
|
expireCookie(ctx, {
|
|
94
96
|
name: multiSessionCookieName,
|
|
95
97
|
attributes: ctx.context.authCookies.sessionToken.attributes
|
|
96
98
|
});
|
|
97
|
-
if (!(ctx.context.session?.session.token ===
|
|
99
|
+
if (!(ctx.context.session?.session.token === sessionCookie)) return ctx.json({ status: true });
|
|
98
100
|
const cookieHeader = ctx.headers?.get("cookie");
|
|
99
101
|
if (cookieHeader) {
|
|
100
102
|
const cookies = Object.fromEntries(parseCookies(cookieHeader));
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { POPUP_TOKEN_STORAGE_KEY } from "./constants.mjs";
|
|
2
|
+
import { OAUTH_POPUP_ERROR_CODES } from "./error-codes.mjs";
|
|
3
|
+
import { oauthPopup } from "./index.mjs";
|
|
4
|
+
import { BetterAuthClientOptions, ClientStore } from "@better-auth/core";
|
|
5
|
+
import * as _better_auth_core_utils_error_codes0 from "@better-auth/core/utils/error-codes";
|
|
6
|
+
import { BetterFetch, BetterFetchPlugin } from "@better-fetch/fetch";
|
|
7
|
+
|
|
8
|
+
//#region src/plugins/oauth-popup/client.d.ts
|
|
9
|
+
/** Inputs for `authClient.signIn.popup`; mirror the redirect sign-in. */
|
|
10
|
+
interface SignInPopupOptions {
|
|
11
|
+
/** Built-in social provider id (e.g. `"google"`). */
|
|
12
|
+
provider?: string;
|
|
13
|
+
/** Generic OAuth provider id (registered via `genericOAuth`). */
|
|
14
|
+
providerId?: string;
|
|
15
|
+
callbackURL?: string;
|
|
16
|
+
errorCallbackURL?: string;
|
|
17
|
+
newUserCallbackURL?: string;
|
|
18
|
+
requestSignUp?: boolean;
|
|
19
|
+
scopes?: string[];
|
|
20
|
+
additionalData?: Record<string, unknown>;
|
|
21
|
+
/** `window.open` feature string; defaults to a centered 500x600 window. */
|
|
22
|
+
windowFeatures?: string;
|
|
23
|
+
/** How long (ms) to wait for the popup to complete. Default 5 minutes. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
interface SignInPopupResult {
|
|
27
|
+
data: {
|
|
28
|
+
success: boolean;
|
|
29
|
+
} | null;
|
|
30
|
+
error: {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
status?: number;
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
/** Reads the stored popup token (browser-only; null otherwise). */
|
|
37
|
+
declare function getStoredPopupToken(): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Attaches the popup token as a bearer header when embedded (where the cookie is
|
|
40
|
+
* partitioned), and clears it once the session ends so it can't be reused.
|
|
41
|
+
*/
|
|
42
|
+
declare const popupBearerFetchPlugin: BetterFetchPlugin;
|
|
43
|
+
interface SignInPopupDeps {
|
|
44
|
+
$fetch: BetterFetch;
|
|
45
|
+
options?: BetterAuthClientOptions | undefined;
|
|
46
|
+
/** Refreshes the reactive session, as the redirect flow's atom listeners do. */
|
|
47
|
+
notifySessionSignal: () => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Builds `signIn.popup`. Runs the sign-in in the popup's own first-party
|
|
51
|
+
* context (so the OAuth state cookie lands there), waits for the completion
|
|
52
|
+
* page to post the session token back, stores it for the bearer fetch plugin,
|
|
53
|
+
* and refreshes the reactive session.
|
|
54
|
+
*/
|
|
55
|
+
declare function createSignInPopup({
|
|
56
|
+
$fetch,
|
|
57
|
+
options,
|
|
58
|
+
notifySessionSignal
|
|
59
|
+
}: SignInPopupDeps): (opts: SignInPopupOptions) => Promise<SignInPopupResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Client plugin for popup OAuth sign-in. Adds `authClient.signIn.popup`. Pair
|
|
62
|
+
* with the server `oauthPopup` and `bearer` plugins.
|
|
63
|
+
*/
|
|
64
|
+
declare const oauthPopupClient: () => {
|
|
65
|
+
id: "oauth-popup";
|
|
66
|
+
version: string;
|
|
67
|
+
$InferServerPlugin: ReturnType<typeof oauthPopup>;
|
|
68
|
+
$ERROR_CODES: {
|
|
69
|
+
POPUP_SIGN_IN_FAILED: _better_auth_core_utils_error_codes0.RawError<"POPUP_SIGN_IN_FAILED">;
|
|
70
|
+
POPUP_BLOCKED: _better_auth_core_utils_error_codes0.RawError<"POPUP_BLOCKED">;
|
|
71
|
+
POPUP_CLOSED: _better_auth_core_utils_error_codes0.RawError<"POPUP_CLOSED">;
|
|
72
|
+
POPUP_TIMEOUT: _better_auth_core_utils_error_codes0.RawError<"POPUP_TIMEOUT">;
|
|
73
|
+
};
|
|
74
|
+
fetchPlugins: BetterFetchPlugin[];
|
|
75
|
+
getActions: ($fetch: BetterFetch, $store: ClientStore, options: BetterAuthClientOptions | undefined) => {
|
|
76
|
+
signIn: {
|
|
77
|
+
popup: (opts: SignInPopupOptions) => Promise<SignInPopupResult>;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { SignInPopupOptions, SignInPopupResult, createSignInPopup, getStoredPopupToken, oauthPopupClient, popupBearerFetchPlugin };
|