better-auth 1.7.0-rc.2 → 1.7.0-rc.4
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 +38 -8
- package/dist/api/rate-limiter/index.mjs +3 -3
- package/dist/api/routes/account.d.mts +4 -4
- package/dist/api/routes/account.mjs +5 -5
- package/dist/api/routes/callback.mjs +2 -2
- package/dist/api/routes/password.mjs +1 -1
- package/dist/api/routes/session.mjs +3 -45
- package/dist/api/routes/sign-in.mjs +2 -2
- package/dist/api/routes/sign-out.d.mts +16 -0
- package/dist/api/routes/sign-out.mjs +68 -2
- package/dist/api/routes/sign-up.mjs +1 -1
- package/dist/api/routes/update-user.mjs +1 -1
- package/dist/client/path-to-object.d.mts +3 -2
- package/dist/client/session-atom.mjs +33 -44
- package/dist/client/solid/index.d.mts +1 -0
- package/dist/client/solid/index.mjs +4 -2
- package/dist/context/create-context.mjs +1 -12
- package/dist/cookies/cache.mjs +25 -0
- package/dist/cookies/index.d.mts +10 -12
- package/dist/cookies/index.mjs +69 -26
- package/dist/cookies/jwt.mjs +44 -0
- package/dist/cookies/session-store.d.mts +1 -1
- package/dist/db/internal-adapter.mjs +29 -21
- package/dist/db/schema.d.mts +1 -1
- package/dist/integrations/next-js.mjs +16 -2
- package/dist/oauth2/account-key.mjs +3 -3
- package/dist/oauth2/link-account.d.mts +1 -1
- package/dist/oauth2/link-account.mjs +9 -9
- package/dist/package.mjs +1 -1
- package/dist/plugins/admin/routes.mjs +2 -2
- package/dist/plugins/anonymous/index.mjs +0 -1
- package/dist/plugins/device-authorization/index.d.mts +41 -2
- package/dist/plugins/device-authorization/index.mjs +3 -3
- package/dist/plugins/device-authorization/routes.mjs +82 -11
- package/dist/plugins/device-authorization/schema.mjs +47 -39
- package/dist/plugins/email-otp/index.mjs +2 -2
- package/dist/plugins/email-otp/otp-token.mjs +1 -1
- package/dist/plugins/email-otp/routes.mjs +11 -6
- package/dist/plugins/generic-oauth/index.d.mts +1 -1
- package/dist/plugins/generic-oauth/index.mjs +22 -0
- package/dist/plugins/generic-oauth/providers/auth0.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/gumroad.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/hubspot.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/keycloak.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/line.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.mjs +35 -9
- package/dist/plugins/generic-oauth/providers/okta.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/patreon.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/slack.mjs +3 -0
- package/dist/plugins/generic-oauth/providers/yandex.mjs +3 -0
- package/dist/plugins/generic-oauth/types.d.mts +27 -0
- package/dist/plugins/index.mjs +2 -2
- package/dist/plugins/jwt/adapter.mjs +7 -6
- package/dist/plugins/jwt/client.d.mts +4 -2
- package/dist/plugins/jwt/client.mjs +1 -1
- package/dist/plugins/jwt/cookie-cache.mjs +24 -64
- package/dist/plugins/jwt/index.d.mts +14 -0
- package/dist/plugins/jwt/index.mjs +16 -3
- package/dist/plugins/jwt/sign.mjs +1 -1
- package/dist/plugins/jwt/types.d.mts +9 -0
- package/dist/plugins/jwt/utils.mjs +2 -2
- package/dist/plugins/oauth-popup/index.mjs +1 -1
- package/dist/plugins/oauth-proxy/index.mjs +15 -5
- package/dist/plugins/one-tap/client.d.mts +4 -3
- package/dist/plugins/one-tap/client.mjs +1 -1
- package/dist/plugins/one-tap/index.mjs +2 -2
- package/dist/plugins/phone-number/routes.mjs +2 -2
- package/dist/plugins/siwe/index.mjs +2 -2
- package/dist/test-utils/http-test-instance.mjs +2 -1
- package/dist/test-utils/test-instance.d.mts +1 -0
- package/dist/test-utils/test-instance.mjs +4 -2
- package/package.json +21 -12
package/dist/api/index.d.mts
CHANGED
|
@@ -290,6 +290,11 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
290
290
|
} | null>;
|
|
291
291
|
readonly signOut: import("better-call").StrictEndpoint<"/sign-out", {
|
|
292
292
|
method: "POST";
|
|
293
|
+
body: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
294
|
+
callbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
295
|
+
disableRedirect: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
296
|
+
state: import("zod").ZodOptional<import("zod").ZodString>;
|
|
297
|
+
}, import("zod/v4/core").$strip>>;
|
|
293
298
|
operationId: string;
|
|
294
299
|
requireHeaders: true;
|
|
295
300
|
metadata: {
|
|
@@ -307,6 +312,14 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
307
312
|
success: {
|
|
308
313
|
type: string;
|
|
309
314
|
};
|
|
315
|
+
url: {
|
|
316
|
+
type: string;
|
|
317
|
+
description: string;
|
|
318
|
+
};
|
|
319
|
+
redirect: {
|
|
320
|
+
type: string;
|
|
321
|
+
description: string;
|
|
322
|
+
};
|
|
310
323
|
};
|
|
311
324
|
};
|
|
312
325
|
};
|
|
@@ -317,6 +330,8 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
317
330
|
};
|
|
318
331
|
}, {
|
|
319
332
|
success: boolean;
|
|
333
|
+
url: string | undefined;
|
|
334
|
+
redirect: boolean | undefined;
|
|
320
335
|
}>;
|
|
321
336
|
readonly signUpEmail: import("better-call").StrictEndpoint<"/sign-up/email", {
|
|
322
337
|
method: "POST";
|
|
@@ -1704,7 +1719,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
1704
1719
|
issuer: {
|
|
1705
1720
|
type: string;
|
|
1706
1721
|
};
|
|
1707
|
-
|
|
1722
|
+
accountId: {
|
|
1708
1723
|
type: string;
|
|
1709
1724
|
};
|
|
1710
1725
|
userId: {
|
|
@@ -1734,7 +1749,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
1734
1749
|
userId: string;
|
|
1735
1750
|
providerId: string;
|
|
1736
1751
|
issuer: string;
|
|
1737
|
-
|
|
1752
|
+
accountId: string;
|
|
1738
1753
|
}[]>;
|
|
1739
1754
|
readonly deleteUserCallback: import("better-call").StrictEndpoint<"/delete-user/callback", {
|
|
1740
1755
|
method: "GET";
|
|
@@ -1984,7 +1999,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
1984
1999
|
issuer: {
|
|
1985
2000
|
type: string;
|
|
1986
2001
|
};
|
|
1987
|
-
|
|
2002
|
+
accountId: {
|
|
1988
2003
|
type: string;
|
|
1989
2004
|
};
|
|
1990
2005
|
};
|
|
@@ -2018,7 +2033,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
2018
2033
|
id: string;
|
|
2019
2034
|
providerId: string;
|
|
2020
2035
|
issuer: string;
|
|
2021
|
-
|
|
2036
|
+
accountId: string;
|
|
2022
2037
|
};
|
|
2023
2038
|
user: import("@better-auth/core/oauth2").OAuth2UserInfo;
|
|
2024
2039
|
data: object;
|
|
@@ -2297,6 +2312,11 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2297
2312
|
} | null>;
|
|
2298
2313
|
readonly signOut: import("better-call").StrictEndpoint<"/sign-out", {
|
|
2299
2314
|
method: "POST";
|
|
2315
|
+
body: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
2316
|
+
callbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
2317
|
+
disableRedirect: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
2318
|
+
state: import("zod").ZodOptional<import("zod").ZodString>;
|
|
2319
|
+
}, import("zod/v4/core").$strip>>;
|
|
2300
2320
|
operationId: string;
|
|
2301
2321
|
requireHeaders: true;
|
|
2302
2322
|
metadata: {
|
|
@@ -2314,6 +2334,14 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2314
2334
|
success: {
|
|
2315
2335
|
type: string;
|
|
2316
2336
|
};
|
|
2337
|
+
url: {
|
|
2338
|
+
type: string;
|
|
2339
|
+
description: string;
|
|
2340
|
+
};
|
|
2341
|
+
redirect: {
|
|
2342
|
+
type: string;
|
|
2343
|
+
description: string;
|
|
2344
|
+
};
|
|
2317
2345
|
};
|
|
2318
2346
|
};
|
|
2319
2347
|
};
|
|
@@ -2324,6 +2352,8 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2324
2352
|
};
|
|
2325
2353
|
}, {
|
|
2326
2354
|
success: boolean;
|
|
2355
|
+
url: string | undefined;
|
|
2356
|
+
redirect: boolean | undefined;
|
|
2327
2357
|
}>;
|
|
2328
2358
|
readonly signUpEmail: import("better-call").StrictEndpoint<"/sign-up/email", {
|
|
2329
2359
|
method: "POST";
|
|
@@ -3711,7 +3741,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
3711
3741
|
issuer: {
|
|
3712
3742
|
type: string;
|
|
3713
3743
|
};
|
|
3714
|
-
|
|
3744
|
+
accountId: {
|
|
3715
3745
|
type: string;
|
|
3716
3746
|
};
|
|
3717
3747
|
userId: {
|
|
@@ -3741,7 +3771,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
3741
3771
|
userId: string;
|
|
3742
3772
|
providerId: string;
|
|
3743
3773
|
issuer: string;
|
|
3744
|
-
|
|
3774
|
+
accountId: string;
|
|
3745
3775
|
}[]>;
|
|
3746
3776
|
readonly deleteUserCallback: import("better-call").StrictEndpoint<"/delete-user/callback", {
|
|
3747
3777
|
method: "GET";
|
|
@@ -3991,7 +4021,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
3991
4021
|
issuer: {
|
|
3992
4022
|
type: string;
|
|
3993
4023
|
};
|
|
3994
|
-
|
|
4024
|
+
accountId: {
|
|
3995
4025
|
type: string;
|
|
3996
4026
|
};
|
|
3997
4027
|
};
|
|
@@ -4025,7 +4055,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
4025
4055
|
id: string;
|
|
4026
4056
|
providerId: string;
|
|
4027
4057
|
issuer: string;
|
|
4028
|
-
|
|
4058
|
+
accountId: string;
|
|
4029
4059
|
};
|
|
4030
4060
|
user: import("@better-auth/core/oauth2").OAuth2UserInfo;
|
|
4031
4061
|
data: object;
|
|
@@ -127,7 +127,7 @@ function createDatabaseStorageWrapper(ctx) {
|
|
|
127
127
|
lastRequest: now
|
|
128
128
|
}
|
|
129
129
|
})) {
|
|
130
|
-
deleteExpiredRows(now);
|
|
130
|
+
await deleteExpiredRows(now);
|
|
131
131
|
return {
|
|
132
132
|
allowed: true,
|
|
133
133
|
retryAfter: null
|
|
@@ -168,9 +168,9 @@ function createDatabaseStorageWrapper(ctx) {
|
|
|
168
168
|
retryAfter: getRetryAfter(fresh.lastRequest, rule.window)
|
|
169
169
|
};
|
|
170
170
|
};
|
|
171
|
-
const deleteExpiredRows = (now) => {
|
|
171
|
+
const deleteExpiredRows = async (now) => {
|
|
172
172
|
const cutoff = now - longestObservedWindow * 1e3;
|
|
173
|
-
ctx.
|
|
173
|
+
await ctx.runInBackgroundOrAwait(db.deleteMany({
|
|
174
174
|
model,
|
|
175
175
|
where: [{
|
|
176
176
|
field: "lastRequest",
|
|
@@ -56,7 +56,7 @@ declare const listUserAccounts: import("better-call").StrictEndpoint<"/list-acco
|
|
|
56
56
|
issuer: {
|
|
57
57
|
type: string;
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
accountId: {
|
|
60
60
|
type: string;
|
|
61
61
|
};
|
|
62
62
|
userId: {
|
|
@@ -86,7 +86,7 @@ declare const listUserAccounts: import("better-call").StrictEndpoint<"/list-acco
|
|
|
86
86
|
userId: string;
|
|
87
87
|
providerId: string;
|
|
88
88
|
issuer: string;
|
|
89
|
-
|
|
89
|
+
accountId: string;
|
|
90
90
|
}[]>;
|
|
91
91
|
declare const linkSocialAccount: import("better-call").StrictEndpoint<"/link-social", {
|
|
92
92
|
method: "POST";
|
|
@@ -375,7 +375,7 @@ declare const accountInfo: import("better-call").StrictEndpoint<"/account-info",
|
|
|
375
375
|
issuer: {
|
|
376
376
|
type: string;
|
|
377
377
|
};
|
|
378
|
-
|
|
378
|
+
accountId: {
|
|
379
379
|
type: string;
|
|
380
380
|
};
|
|
381
381
|
};
|
|
@@ -409,7 +409,7 @@ declare const accountInfo: import("better-call").StrictEndpoint<"/account-info",
|
|
|
409
409
|
id: string;
|
|
410
410
|
providerId: string;
|
|
411
411
|
issuer: string;
|
|
412
|
-
|
|
412
|
+
accountId: string;
|
|
413
413
|
};
|
|
414
414
|
user: import("@better-auth/core/oauth2").OAuth2UserInfo;
|
|
415
415
|
data: object;
|
|
@@ -42,7 +42,7 @@ const listUserAccounts = createAuthEndpoint("/list-accounts", {
|
|
|
42
42
|
format: "date-time"
|
|
43
43
|
},
|
|
44
44
|
issuer: { type: "string" },
|
|
45
|
-
|
|
45
|
+
accountId: { type: "string" },
|
|
46
46
|
userId: { type: "string" },
|
|
47
47
|
scopes: {
|
|
48
48
|
type: "array",
|
|
@@ -55,7 +55,7 @@ const listUserAccounts = createAuthEndpoint("/list-accounts", {
|
|
|
55
55
|
"createdAt",
|
|
56
56
|
"updatedAt",
|
|
57
57
|
"issuer",
|
|
58
|
-
"
|
|
58
|
+
"accountId",
|
|
59
59
|
"userId",
|
|
60
60
|
"scopes"
|
|
61
61
|
]
|
|
@@ -559,13 +559,13 @@ const accountInfo = createAuthEndpoint("/account-info", {
|
|
|
559
559
|
id: { type: "string" },
|
|
560
560
|
providerId: { type: "string" },
|
|
561
561
|
issuer: { type: "string" },
|
|
562
|
-
|
|
562
|
+
accountId: { type: "string" }
|
|
563
563
|
},
|
|
564
564
|
required: [
|
|
565
565
|
"id",
|
|
566
566
|
"providerId",
|
|
567
567
|
"issuer",
|
|
568
|
-
"
|
|
568
|
+
"accountId"
|
|
569
569
|
],
|
|
570
570
|
additionalProperties: false
|
|
571
571
|
},
|
|
@@ -617,7 +617,7 @@ const accountInfo = createAuthEndpoint("/account-info", {
|
|
|
617
617
|
id: account.id,
|
|
618
618
|
providerId: account.providerId,
|
|
619
619
|
issuer: account.issuer,
|
|
620
|
-
|
|
620
|
+
accountId: account.accountId
|
|
621
621
|
}
|
|
622
622
|
});
|
|
623
623
|
});
|
|
@@ -142,7 +142,7 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
|
|
|
142
142
|
});
|
|
143
143
|
return redirectOnError(OAUTH_CALLBACK_ERROR_CODES.UNABLE_TO_GET_USER_INFO);
|
|
144
144
|
}
|
|
145
|
-
const {
|
|
145
|
+
const { accountId } = accountKey;
|
|
146
146
|
if (!callbackURL) {
|
|
147
147
|
c.context.logger.error("No callback URL found");
|
|
148
148
|
throw redirectOnError(OAUTH_CALLBACK_ERROR_CODES.NO_CALLBACK_URL);
|
|
@@ -220,7 +220,7 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
|
|
|
220
220
|
result = await handleOAuthUserInfo(c, {
|
|
221
221
|
userInfo: {
|
|
222
222
|
...userInfo,
|
|
223
|
-
id:
|
|
223
|
+
id: accountId,
|
|
224
224
|
email: userInfo.email,
|
|
225
225
|
name: userInfo.name || ""
|
|
226
226
|
},
|
|
@@ -165,7 +165,7 @@ const resetPassword = createAuthEndpoint("/reset-password", {
|
|
|
165
165
|
userId,
|
|
166
166
|
providerId: "credential",
|
|
167
167
|
issuer: createLocalAccountIssuer("credential"),
|
|
168
|
-
|
|
168
|
+
accountId: user.id,
|
|
169
169
|
password: hashedPassword
|
|
170
170
|
});
|
|
171
171
|
else await ctx.context.internalAdapter.updatePassword(userId, hashedPassword);
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { isAPIError } from "../../utils/is-api-error.mjs";
|
|
2
2
|
import { hasServerSessionStore } from "../../context/store-capabilities.mjs";
|
|
3
|
-
import { symmetricDecodeJWT, verifyJWT } from "../../crypto/jwt.mjs";
|
|
4
3
|
import { parseSessionOutput, parseUserOutput } from "../../db/schema.mjs";
|
|
5
|
-
import { getCookieCacheJwtSigningKey, verifyCookieCacheJWT } from "../../plugins/jwt/cookie-cache.mjs";
|
|
6
4
|
import { getDate } from "../../utils/date.mjs";
|
|
7
5
|
import { getChunkedCookie, getSessionQuerySchema } from "../../cookies/session-store.mjs";
|
|
8
|
-
import { deleteSessionCookie, expireCookie, setCookieCache, setSessionCookie } from "../../cookies/index.mjs";
|
|
6
|
+
import { decodeCookieCache, deleteSessionCookie, expireCookie, setCookieCache, setSessionCookie } from "../../cookies/index.mjs";
|
|
9
7
|
import { getShouldSkipSessionRefresh } from "../state/should-session-refresh.mjs";
|
|
10
8
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
11
|
-
import { safeJSONParse } from "@better-auth/core/utils/json";
|
|
12
9
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
13
10
|
import * as z from "zod";
|
|
14
|
-
import { base64Url } from "@better-auth/utils/base64";
|
|
15
|
-
import { binary } from "@better-auth/utils/binary";
|
|
16
|
-
import { createHMAC } from "@better-auth/utils/hmac";
|
|
17
11
|
//#region src/api/routes/session.ts
|
|
18
12
|
const getSession = () => createAuthEndpoint("/get-session", {
|
|
19
13
|
method: ["GET", "POST"],
|
|
@@ -45,44 +39,8 @@ const getSession = () => createAuthEndpoint("/get-session", {
|
|
|
45
39
|
const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret);
|
|
46
40
|
if (!sessionCookieToken) return null;
|
|
47
41
|
const sessionDataCookie = getChunkedCookie(ctx, ctx.context.authCookies.sessionData.name);
|
|
48
|
-
|
|
49
|
-
if (sessionDataCookie)
|
|
50
|
-
const strategy = ctx.context.options.session?.cookieCache?.strategy || "compact";
|
|
51
|
-
const jwtSigningKey = getCookieCacheJwtSigningKey(ctx.context.options);
|
|
52
|
-
if (strategy === "jwe") {
|
|
53
|
-
const payload = await symmetricDecodeJWT(sessionDataCookie, ctx.context.secretConfig, "better-auth-session");
|
|
54
|
-
if (payload && payload.session && payload.user) sessionDataPayload = {
|
|
55
|
-
session: {
|
|
56
|
-
session: payload.session,
|
|
57
|
-
user: payload.user,
|
|
58
|
-
updatedAt: payload.updatedAt,
|
|
59
|
-
version: payload.version
|
|
60
|
-
},
|
|
61
|
-
expiresAt: payload.exp ? payload.exp * 1e3 : Date.now()
|
|
62
|
-
};
|
|
63
|
-
else expireCookie(ctx, ctx.context.authCookies.sessionData);
|
|
64
|
-
} else if (strategy === "jwt") {
|
|
65
|
-
const payload = jwtSigningKey === "jwt-plugin" ? await verifyCookieCacheJWT(ctx, sessionDataCookie) : await verifyJWT(sessionDataCookie, ctx.context.secret);
|
|
66
|
-
if (payload && payload.session && payload.user) sessionDataPayload = {
|
|
67
|
-
session: {
|
|
68
|
-
session: payload.session,
|
|
69
|
-
user: payload.user,
|
|
70
|
-
updatedAt: payload.updatedAt,
|
|
71
|
-
version: payload.version
|
|
72
|
-
},
|
|
73
|
-
expiresAt: payload.exp ? payload.exp * 1e3 : Date.now()
|
|
74
|
-
};
|
|
75
|
-
else expireCookie(ctx, ctx.context.authCookies.sessionData);
|
|
76
|
-
} else {
|
|
77
|
-
const parsed = safeJSONParse(binary.decode(base64Url.decode(sessionDataCookie)));
|
|
78
|
-
if (parsed) if (await createHMAC("SHA-256", "base64urlnopad").verify(ctx.context.secret, JSON.stringify({
|
|
79
|
-
...parsed.session,
|
|
80
|
-
expiresAt: parsed.expiresAt
|
|
81
|
-
}), parsed.signature)) sessionDataPayload = parsed;
|
|
82
|
-
else expireCookie(ctx, ctx.context.authCookies.sessionData);
|
|
83
|
-
else expireCookie(ctx, ctx.context.authCookies.sessionData);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
42
|
+
const sessionDataPayload = sessionDataCookie ? await decodeCookieCache(ctx, sessionDataCookie) : null;
|
|
43
|
+
if (sessionDataCookie && !sessionDataPayload) expireCookie(ctx, ctx.context.authCookies.sessionData);
|
|
86
44
|
const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret);
|
|
87
45
|
/**
|
|
88
46
|
* If session data is present in the cookie, check if it should be used or refreshed
|
|
@@ -182,7 +182,7 @@ const signInSocial = () => createAuthEndpoint("/sign-in/social", {
|
|
|
182
182
|
userInfo: {
|
|
183
183
|
...userInfo.user,
|
|
184
184
|
email: userInfo.user.email,
|
|
185
|
-
id: accountKey.
|
|
185
|
+
id: accountKey.accountId,
|
|
186
186
|
name: userInfo.user.name || "",
|
|
187
187
|
image: userInfo.user.image,
|
|
188
188
|
emailVerified: userInfo.user.emailVerified || false
|
|
@@ -316,7 +316,7 @@ const signInEmail = () => createAuthEndpoint("/sign-in/email", {
|
|
|
316
316
|
if (!z.email().safeParse(email).success) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_EMAIL);
|
|
317
317
|
const userRecord = await ctx.context.internalAdapter.findUserByEmail(email.toLowerCase(), { includeAccounts: true });
|
|
318
318
|
const credentialIssuer = createLocalAccountIssuer("credential");
|
|
319
|
-
const credentialAccount = userRecord?.accounts.find((account) => account.providerId === "credential" && account.issuer === credentialIssuer && account.
|
|
319
|
+
const credentialAccount = userRecord?.accounts.find((account) => account.providerId === "credential" && account.issuer === credentialIssuer && account.accountId === userRecord.user.id);
|
|
320
320
|
if (!userRecord || !credentialAccount) {
|
|
321
321
|
await ctx.context.password.hash(password);
|
|
322
322
|
ctx.context.logger.warn("User not found");
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
1
2
|
//#region src/api/routes/sign-out.d.ts
|
|
2
3
|
declare const signOut: import("better-call").StrictEndpoint<"/sign-out", {
|
|
3
4
|
method: "POST";
|
|
5
|
+
body: z.ZodOptional<z.ZodObject<{
|
|
6
|
+
callbackURL: z.ZodOptional<z.ZodString>;
|
|
7
|
+
disableRedirect: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
state: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>>;
|
|
4
10
|
operationId: string;
|
|
5
11
|
requireHeaders: true;
|
|
6
12
|
metadata: {
|
|
@@ -18,6 +24,14 @@ declare const signOut: import("better-call").StrictEndpoint<"/sign-out", {
|
|
|
18
24
|
success: {
|
|
19
25
|
type: string;
|
|
20
26
|
};
|
|
27
|
+
url: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
redirect: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
21
35
|
};
|
|
22
36
|
};
|
|
23
37
|
};
|
|
@@ -28,6 +42,8 @@ declare const signOut: import("better-call").StrictEndpoint<"/sign-out", {
|
|
|
28
42
|
};
|
|
29
43
|
}, {
|
|
30
44
|
success: boolean;
|
|
45
|
+
url: string | undefined;
|
|
46
|
+
redirect: boolean | undefined;
|
|
31
47
|
}>;
|
|
32
48
|
//#endregion
|
|
33
49
|
export { signOut };
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { deleteSessionCookie } from "../../cookies/index.mjs";
|
|
2
2
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
3
|
+
import * as z from "zod";
|
|
3
4
|
//#region src/api/routes/sign-out.ts
|
|
5
|
+
const signOutBodySchema = z.object({
|
|
6
|
+
callbackURL: z.string().meta({ description: "The URL to redirect to after provider logout" }).optional(),
|
|
7
|
+
disableRedirect: z.boolean().meta({ description: "Return the provider logout URL without redirecting" }).optional(),
|
|
8
|
+
state: z.string().meta({ description: "State to pass to the provider logout endpoint" }).optional()
|
|
9
|
+
}).optional();
|
|
10
|
+
const signOutResponse = (providerLogout) => ({
|
|
11
|
+
success: true,
|
|
12
|
+
url: providerLogout?.url,
|
|
13
|
+
redirect: providerLogout?.redirect
|
|
14
|
+
});
|
|
4
15
|
const signOut = createAuthEndpoint("/sign-out", {
|
|
5
16
|
method: "POST",
|
|
17
|
+
body: signOutBodySchema,
|
|
6
18
|
operationId: "signOut",
|
|
7
19
|
requireHeaders: true,
|
|
8
20
|
metadata: { openapi: {
|
|
@@ -12,19 +24,73 @@ const signOut = createAuthEndpoint("/sign-out", {
|
|
|
12
24
|
description: "Success",
|
|
13
25
|
content: { "application/json": { schema: {
|
|
14
26
|
type: "object",
|
|
15
|
-
properties: {
|
|
27
|
+
properties: {
|
|
28
|
+
success: { type: "boolean" },
|
|
29
|
+
url: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Provider logout URL when RP-initiated logout is available"
|
|
32
|
+
},
|
|
33
|
+
redirect: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
description: "Whether the client should redirect to the provider logout URL"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
16
38
|
} } }
|
|
17
39
|
} }
|
|
18
40
|
} }
|
|
19
41
|
}, async (ctx) => {
|
|
20
42
|
const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret);
|
|
43
|
+
let currentSession = null;
|
|
44
|
+
if (sessionCookieToken) try {
|
|
45
|
+
currentSession = await ctx.context.internalAdapter.findSession(sessionCookieToken);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
ctx.context.logger.error("Failed to read session from database", e);
|
|
48
|
+
}
|
|
21
49
|
if (sessionCookieToken) try {
|
|
22
50
|
await ctx.context.internalAdapter.deleteSession(sessionCookieToken);
|
|
23
51
|
} catch (e) {
|
|
24
52
|
ctx.context.logger.error("Failed to delete session from database", e);
|
|
25
53
|
}
|
|
26
54
|
deleteSessionCookie(ctx);
|
|
27
|
-
|
|
55
|
+
const providerLogoutResult = await (async () => {
|
|
56
|
+
try {
|
|
57
|
+
if (!currentSession) return null;
|
|
58
|
+
const accounts = await ctx.context.internalAdapter.findAccounts(currentSession.user.id);
|
|
59
|
+
const providersById = new Map(ctx.context.socialProviders.map((provider) => [provider.id, provider]));
|
|
60
|
+
const logoutAccounts = accounts.filter((account) => Boolean(providersById.get(account.providerId)?.createEndSessionURL)).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
61
|
+
if (logoutAccounts.length === 0) return null;
|
|
62
|
+
const seenProviderIds = /* @__PURE__ */ new Set();
|
|
63
|
+
const postLogoutRedirectURI = ctx.body?.callbackURL ? new URL(ctx.body.callbackURL, ctx.context.baseURL).toString() : void 0;
|
|
64
|
+
for (const account of logoutAccounts) {
|
|
65
|
+
if (seenProviderIds.has(account.providerId)) continue;
|
|
66
|
+
seenProviderIds.add(account.providerId);
|
|
67
|
+
const provider = providersById.get(account.providerId);
|
|
68
|
+
try {
|
|
69
|
+
const url = await provider?.createEndSessionURL?.({
|
|
70
|
+
idToken: account.idToken,
|
|
71
|
+
postLogoutRedirectURI,
|
|
72
|
+
state: ctx.body?.state
|
|
73
|
+
});
|
|
74
|
+
if (url) return url.toString();
|
|
75
|
+
} catch (e) {
|
|
76
|
+
ctx.context.logger.error(`Failed to create logout URL for provider "${account.providerId}"`, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
ctx.context.logger.error("Failed to create provider logout URL", e);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
})();
|
|
85
|
+
if (providerLogoutResult) {
|
|
86
|
+
const shouldRedirect = !ctx.body?.disableRedirect;
|
|
87
|
+
if (shouldRedirect) ctx.setHeader("Location", providerLogoutResult);
|
|
88
|
+
return ctx.json(signOutResponse({
|
|
89
|
+
url: providerLogoutResult,
|
|
90
|
+
redirect: shouldRedirect
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return ctx.json(signOutResponse());
|
|
28
94
|
});
|
|
29
95
|
//#endregion
|
|
30
96
|
export { signOut };
|
|
@@ -244,7 +244,7 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
|
|
|
244
244
|
userId: createdUser.id,
|
|
245
245
|
providerId: "credential",
|
|
246
246
|
issuer: createLocalAccountIssuer("credential"),
|
|
247
|
-
|
|
247
|
+
accountId: createdUser.id,
|
|
248
248
|
password: hash
|
|
249
249
|
});
|
|
250
250
|
if (ctx.context.options.emailVerification?.sendOnSignUp ?? ctx.context.options.emailAndPassword.requireEmailVerification) {
|
|
@@ -221,7 +221,7 @@ newPassword: z.string().meta({ description: "The new password to set is required
|
|
|
221
221
|
userId: session.user.id,
|
|
222
222
|
providerId: "credential",
|
|
223
223
|
issuer: createLocalAccountIssuer("credential"),
|
|
224
|
-
|
|
224
|
+
accountId: session.user.id,
|
|
225
225
|
password: passwordHash
|
|
226
226
|
});
|
|
227
227
|
return ctx.json({ status: true });
|
|
@@ -77,7 +77,8 @@ type InferBodyCtx<Body, FetchOptions extends ClientFetchOption> = Body extends R
|
|
|
77
77
|
} : never;
|
|
78
78
|
type PrettifyUnion<T> = T extends unknown ? Prettify$1<T> : never;
|
|
79
79
|
type HasRequiredKeysInUnion<T> = true extends (T extends unknown ? HasRequiredKeys<T> : never) ? true : false;
|
|
80
|
-
type
|
|
80
|
+
type HasRequiredCtx<C extends InputContext<any, any>, FetchOptions extends ClientFetchOption> = IsAny<C["body"]> extends true ? HasRequiredKeysInUnion<InferCtx<C, FetchOptions>> : undefined extends C["body"] ? HasRequiredKeysInUnion<InferCtxQuery<C, FetchOptions>> : HasRequiredKeysInUnion<InferCtx<C, FetchOptions>>;
|
|
81
|
+
type InferCtx<C extends InputContext<any, any>, FetchOptions extends ClientFetchOption> = IsAny<C["body"]> extends true ? InferCtxQuery<C, FetchOptions> : [NonNullable<C["body"]>] extends [never] ? InferCtxQuery<C, FetchOptions> : NonNullable<C["body"]> extends Record<string, any> ? InferBodyCtx<NonNullable<C["body"]>, FetchOptions> : InferCtxQuery<C, FetchOptions>;
|
|
81
82
|
type MergeRoutes<T> = UnionToIntersection<T>;
|
|
82
83
|
type InferRoute<API, COpts extends BetterAuthClientOptions> = API extends Record<string, infer T> ? T extends Endpoint ? T["options"]["metadata"] extends {
|
|
83
84
|
isAction: false;
|
|
@@ -87,7 +88,7 @@ type InferRoute<API, COpts extends BetterAuthClientOptions> = API extends Record
|
|
|
87
88
|
scope: "http";
|
|
88
89
|
} | {
|
|
89
90
|
scope: "server";
|
|
90
|
-
} ? {} : PathToObject<T["path"], T extends ((ctx: infer C) => infer R) ? C extends InputContext<any, any> ? <FetchOptions extends ClientFetchOption<Partial<C["body"]> & Record<string, any>, Partial<C["query"]> & Record<string, any>, C["params"]>>(...data:
|
|
91
|
+
} ? {} : PathToObject<T["path"], T extends ((ctx: infer C) => infer R) ? C extends InputContext<any, any> ? <FetchOptions extends ClientFetchOption<Partial<C["body"]> & Record<string, any>, Partial<C["query"]> & Record<string, any>, C["params"]>>(...data: HasRequiredCtx<C, FetchOptions> extends true ? [PrettifyUnion<T["path"] extends `/sign-up/email` ? InferSignUpEmailCtx<COpts, FetchOptions> : T["path"] extends `/sign-in/social` ? Omit<InferCtx<C, FetchOptions>, "provider"> & {
|
|
91
92
|
provider: SocialProviderList[number] | InferGenericOAuthProviderIds<COpts> | (string & {});
|
|
92
93
|
fetchOptions?: FetchOptions | undefined;
|
|
93
94
|
} : InferCtx<C, FetchOptions>>, FetchOptions?] : [PrettifyUnion<T["path"] extends `/update-user` ? InferUserUpdateCtx<COpts, FetchOptions> : T["path"] extends `/update-session` ? InferSessionUpdateCtx<COpts, FetchOptions> : InferCtx<C, FetchOptions>>?, FetchOptions?]) => Promise<BetterFetchResponse<T["options"]["metadata"] extends {
|