@better-auth-ui/core 1.6.30 → 1.6.31
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/{create-auth-plugin-DcxR23rZ.js → create-auth-plugin-D4wsYnHD.js} +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +46 -15
- package/dist/lib/auth-mutation-keys.d.ts +5 -4
- package/dist/lib/auth-redirect.d.ts +31 -0
- package/dist/lib/view-paths.d.ts +5 -0
- package/dist/plugins/device-authorization/device-authorization-localization.d.ts +33 -0
- package/dist/plugins/device-authorization/device-authorization-mutation-keys.d.ts +16 -0
- package/dist/plugins/device-authorization/device-authorization-plugin.d.ts +59 -0
- package/dist/plugins.d.ts +3 -0
- package/dist/plugins.js +95 -51
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/auth-mutation-keys.ts +5 -4
- package/src/lib/auth-redirect.ts +105 -0
- package/src/lib/view-paths.ts +6 -0
- package/src/plugins/device-authorization/device-authorization-localization.ts +36 -0
- package/src/plugins/device-authorization/device-authorization-mutation-keys.ts +16 -0
- package/src/plugins/device-authorization/device-authorization-plugin.ts +65 -0
- package/src/plugins.ts +3 -0
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './config';
|
|
|
2
2
|
export * from './lib/auth-mutation-keys';
|
|
3
3
|
export * from './lib/auth-plugin';
|
|
4
4
|
export * from './lib/auth-query-keys';
|
|
5
|
+
export * from './lib/auth-redirect';
|
|
5
6
|
export * from './lib/base-paths';
|
|
6
7
|
export * from './lib/create-auth-plugin';
|
|
7
8
|
export * from './lib/deep-partial';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./create-auth-plugin-
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./create-auth-plugin-D4wsYnHD.js";
|
|
2
2
|
//#region src/config/additional-fields-config.ts
|
|
3
3
|
function l(e, t) {
|
|
4
4
|
if (e.type === "boolean") return t === "on" || t === "true";
|
|
@@ -59,7 +59,38 @@ var d = {
|
|
|
59
59
|
revokeSession: ["auth", "revokeSession"],
|
|
60
60
|
unlinkAccount: ["auth", "unlinkAccount"],
|
|
61
61
|
updateUser: ["auth", "updateUser"]
|
|
62
|
-
}, f =
|
|
62
|
+
}, f = /^https?:\/\//i;
|
|
63
|
+
function p(e) {
|
|
64
|
+
for (let t of e) {
|
|
65
|
+
let e = t.codePointAt(0);
|
|
66
|
+
if (t === "\\" || e === void 0 || e <= 31 || e === 127) return !0;
|
|
67
|
+
}
|
|
68
|
+
return !1;
|
|
69
|
+
}
|
|
70
|
+
function m(e, t) {
|
|
71
|
+
if (!e || p(e)) return "/";
|
|
72
|
+
let n = e.trim();
|
|
73
|
+
if (!n || n.startsWith("//") || !n.startsWith("/") && !f.test(n)) return "/";
|
|
74
|
+
try {
|
|
75
|
+
let e = new URL(t), r = new URL(n, e);
|
|
76
|
+
return r.origin !== e.origin || r.username || r.password ? "/" : `${r.pathname}${r.search}${r.hash}`;
|
|
77
|
+
} catch {
|
|
78
|
+
return "/";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function h(e, t, n) {
|
|
82
|
+
let r = m(e.searchParams.get("redirectTo"), e.origin), i = new URL(r, e.origin).pathname === e.pathname ? "/" : r;
|
|
83
|
+
if (t) return {
|
|
84
|
+
type: "redirect",
|
|
85
|
+
to: i
|
|
86
|
+
};
|
|
87
|
+
let a = new URL(n, e.origin);
|
|
88
|
+
return a.searchParams.set("redirectTo", `${e.pathname}${e.search}${e.hash}`), {
|
|
89
|
+
type: "signIn",
|
|
90
|
+
to: `${a.pathname}${a.search}${a.hash}`
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
var g = {
|
|
63
94
|
version: "2.0",
|
|
64
95
|
providers: [
|
|
65
96
|
{
|
|
@@ -1476,26 +1507,26 @@ var d = {
|
|
|
1476
1507
|
domains: 223,
|
|
1477
1508
|
generated: "2025-12-09T10:38:50.000Z"
|
|
1478
1509
|
}
|
|
1479
|
-
},
|
|
1480
|
-
function
|
|
1481
|
-
if (
|
|
1482
|
-
|
|
1483
|
-
let { providers: e } =
|
|
1484
|
-
for (let t of e) if (t.loginUrl) for (let e of t.domains ?? [])
|
|
1510
|
+
}, _;
|
|
1511
|
+
function v() {
|
|
1512
|
+
if (_) return _;
|
|
1513
|
+
_ = /* @__PURE__ */ new Map();
|
|
1514
|
+
let { providers: e } = g;
|
|
1515
|
+
for (let t of e) if (t.loginUrl) for (let e of t.domains ?? []) _.set(e.toLowerCase(), {
|
|
1485
1516
|
companyProvider: t.companyProvider,
|
|
1486
1517
|
loginUrl: t.loginUrl
|
|
1487
1518
|
});
|
|
1488
|
-
return
|
|
1519
|
+
return _;
|
|
1489
1520
|
}
|
|
1490
|
-
function
|
|
1521
|
+
function y(e) {
|
|
1491
1522
|
let t = e.lastIndexOf("@");
|
|
1492
1523
|
if (t === -1) return null;
|
|
1493
1524
|
let n = e.slice(t + 1).trim().toLowerCase();
|
|
1494
|
-
return n ?
|
|
1525
|
+
return n ? v().get(n) ?? null : null;
|
|
1495
1526
|
}
|
|
1496
1527
|
//#endregion
|
|
1497
1528
|
//#region src/lib/provider-names.ts
|
|
1498
|
-
var
|
|
1529
|
+
var b = {
|
|
1499
1530
|
apple: "Apple",
|
|
1500
1531
|
atlassian: "Atlassian",
|
|
1501
1532
|
cognito: "Cognito",
|
|
@@ -1532,8 +1563,8 @@ var g = {
|
|
|
1532
1563
|
wechat: "WeChat",
|
|
1533
1564
|
zoom: "Zoom"
|
|
1534
1565
|
};
|
|
1535
|
-
function
|
|
1536
|
-
return
|
|
1566
|
+
function x(e) {
|
|
1567
|
+
return b[e] || e.charAt(0).toUpperCase() + e.slice(1);
|
|
1537
1568
|
}
|
|
1538
1569
|
//#endregion
|
|
1539
|
-
export { d as authMutationKeys, i as authQueryKeys, r as basePaths, c as createAuthPlugin, e as deepmerge, o as defaultAuthConfig, a as fileToBase64, h as getEmailProviderLink,
|
|
1570
|
+
export { d as authMutationKeys, i as authQueryKeys, r as basePaths, c as createAuthPlugin, e as deepmerge, o as defaultAuthConfig, a as fileToBase64, h as getAuthRedirectAction, y as getEmailProviderLink, x as getProviderName, m as getSafeRedirectTo, t as localization, l as parseAdditionalFieldValue, b as providerNames, s as resizeAvatar, u as resolveInputType, n as viewPaths };
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
* package the mutation options factory came from.
|
|
20
20
|
*
|
|
21
21
|
* Plugin-specific mutation keys live alongside their plugin (e.g.
|
|
22
|
-
* `apiKeyMutationKeys`, `
|
|
23
|
-
* `
|
|
24
|
-
* `deleteUserMutationKeys`).
|
|
25
|
-
*
|
|
22
|
+
* `apiKeyMutationKeys`, `deviceAuthorizationMutationKeys`,
|
|
23
|
+
* `organizationMutationKeys`, `passkeyMutationKeys`, `magicLinkMutationKeys`,
|
|
24
|
+
* `multiSessionMutationKeys`, `usernameMutationKeys`, `deleteUserMutationKeys`).
|
|
25
|
+
* Plugin-contributed sign-in strategies stay under the shared
|
|
26
|
+
* `["auth", "signIn", ...]` namespace so
|
|
26
27
|
* `useIsMutating({ mutationKey: authMutationKeys.signIn.all })` still
|
|
27
28
|
* matches them.
|
|
28
29
|
*
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a redirect target to a same-origin path.
|
|
3
|
+
*
|
|
4
|
+
* Root-relative paths and same-origin HTTP(S) URLs are accepted. Invalid,
|
|
5
|
+
* cross-origin, protocol-relative, and non-HTTP targets fall back to `/`.
|
|
6
|
+
*
|
|
7
|
+
* @param redirectTo - Requested redirect target
|
|
8
|
+
* @param origin - Origin used to validate and normalize the target
|
|
9
|
+
* @returns A same-origin path including its query string and hash
|
|
10
|
+
*/
|
|
11
|
+
export declare function getSafeRedirectTo(redirectTo: string | null | undefined, origin: string): string;
|
|
12
|
+
export type AuthRedirectAction = {
|
|
13
|
+
type: "redirect";
|
|
14
|
+
to: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: "signIn";
|
|
17
|
+
to: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the next action for the authenticated redirect view.
|
|
21
|
+
*
|
|
22
|
+
* Signed-in users continue to the validated target. Signed-out users are sent
|
|
23
|
+
* to sign in with the current redirect-view URL preserved, allowing the view
|
|
24
|
+
* to perform a full-page redirect after authentication.
|
|
25
|
+
*
|
|
26
|
+
* @param currentURL - Current redirect-view URL
|
|
27
|
+
* @param authenticated - Whether the current user has a session
|
|
28
|
+
* @param signInPath - Same-origin path to the sign-in view
|
|
29
|
+
* @returns The redirect or sign-in action to perform
|
|
30
|
+
*/
|
|
31
|
+
export declare function getAuthRedirectAction(currentURL: URL, authenticated: boolean, signInPath: string): AuthRedirectAction;
|
package/dist/lib/view-paths.d.ts
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* View path segments for authentication routes.
|
|
3
3
|
*/
|
|
4
4
|
export interface AuthViewPaths {
|
|
5
|
+
/**
|
|
6
|
+
* Path segment for the authenticated redirect view
|
|
7
|
+
* @default "redirect"
|
|
8
|
+
*/
|
|
9
|
+
redirect: string;
|
|
5
10
|
/**
|
|
6
11
|
* Path segment for the sign-in view
|
|
7
12
|
* @default "sign-in"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const deviceAuthorizationLocalization: {
|
|
2
|
+
/** @remarks `"Device Authorization"` */
|
|
3
|
+
deviceAuthorization: string;
|
|
4
|
+
/** @remarks `"Enter the code displayed on your device."` */
|
|
5
|
+
deviceAuthorizationDescription: string;
|
|
6
|
+
/** @remarks `"Device code"` */
|
|
7
|
+
deviceCode: string;
|
|
8
|
+
/** @remarks `"The code is invalid or has expired."` */
|
|
9
|
+
invalidDeviceCode: string;
|
|
10
|
+
/** @remarks `"Continue"` */
|
|
11
|
+
continue: string;
|
|
12
|
+
/** @remarks `"Approve Device"` */
|
|
13
|
+
approveDevice: string;
|
|
14
|
+
/** @remarks `"A device is requesting access to your account."` */
|
|
15
|
+
approveDeviceDescription: string;
|
|
16
|
+
/** @remarks `"Signed in as"` */
|
|
17
|
+
signedInAs: string;
|
|
18
|
+
/** @remarks `"Approve"` */
|
|
19
|
+
approve: string;
|
|
20
|
+
/** @remarks `"Deny"` */
|
|
21
|
+
deny: string;
|
|
22
|
+
/** @remarks `"Device Approved"` */
|
|
23
|
+
deviceApproved: string;
|
|
24
|
+
/** @remarks `"The device can now access your account. You can return to it and continue."` */
|
|
25
|
+
deviceApprovedDescription: string;
|
|
26
|
+
/** @remarks `"Device Denied"` */
|
|
27
|
+
deviceDenied: string;
|
|
28
|
+
/** @remarks `"The device was not granted access to your account."` */
|
|
29
|
+
deviceDeniedDescription: string;
|
|
30
|
+
/** @remarks `"Return to application"` */
|
|
31
|
+
returnToApplication: string;
|
|
32
|
+
};
|
|
33
|
+
export type DeviceAuthorizationLocalization = typeof deviceAuthorizationLocalization;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mutation keys contributed by the device-authorization plugin.
|
|
3
|
+
*
|
|
4
|
+
* All keys live under the shared `["auth"]` namespace so global auth
|
|
5
|
+
* mutation observers handle errors consistently.
|
|
6
|
+
*/
|
|
7
|
+
export declare const deviceAuthorizationMutationKeys: {
|
|
8
|
+
/** Prefix matching every device-authorization mutation. */
|
|
9
|
+
readonly all: readonly ["auth", "deviceAuthorization"];
|
|
10
|
+
/** Key for verifying and claiming a user code. */
|
|
11
|
+
readonly verify: readonly ["auth", "deviceAuthorization", "verify"];
|
|
12
|
+
/** Key for approving a pending device request. */
|
|
13
|
+
readonly approve: readonly ["auth", "deviceAuthorization", "approve"];
|
|
14
|
+
/** Key for denying a pending device request. */
|
|
15
|
+
readonly deny: readonly ["auth", "deviceAuthorization", "deny"];
|
|
16
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DeviceAuthorizationLocalization } from './device-authorization-localization';
|
|
2
|
+
declare module "../../lib/view-paths" {
|
|
3
|
+
/** Widens `AuthViewPaths` with the device-authorization path when this plugin is imported. */
|
|
4
|
+
interface AuthViewPaths {
|
|
5
|
+
/** @default "device" */
|
|
6
|
+
deviceAuthorization?: string;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export type DeviceAuthorizationPluginOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Override the plugin's default localization strings.
|
|
12
|
+
* @remarks `DeviceAuthorizationLocalization`
|
|
13
|
+
*/
|
|
14
|
+
localization?: Partial<DeviceAuthorizationLocalization>;
|
|
15
|
+
/**
|
|
16
|
+
* URL segment for the device-authorization view.
|
|
17
|
+
* @remarks `string`
|
|
18
|
+
* @default "device"
|
|
19
|
+
*/
|
|
20
|
+
path?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Number of characters rendered by the device-code input.
|
|
23
|
+
*
|
|
24
|
+
* Keep this in sync with Better Auth's `deviceAuthorization({ userCodeLength })`
|
|
25
|
+
* server option.
|
|
26
|
+
*
|
|
27
|
+
* @default 8
|
|
28
|
+
*/
|
|
29
|
+
userCodeLength?: number;
|
|
30
|
+
};
|
|
31
|
+
export declare const deviceAuthorizationPlugin: ((options?: DeviceAuthorizationPluginOptions | undefined) => {
|
|
32
|
+
localization: {
|
|
33
|
+
deviceAuthorization: string;
|
|
34
|
+
deviceAuthorizationDescription: string;
|
|
35
|
+
deviceCode: string;
|
|
36
|
+
invalidDeviceCode: string;
|
|
37
|
+
continue: string;
|
|
38
|
+
approveDevice: string;
|
|
39
|
+
approveDeviceDescription: string;
|
|
40
|
+
signedInAs: string;
|
|
41
|
+
approve: string;
|
|
42
|
+
deny: string;
|
|
43
|
+
deviceApproved: string;
|
|
44
|
+
deviceApprovedDescription: string;
|
|
45
|
+
deviceDenied: string;
|
|
46
|
+
deviceDeniedDescription: string;
|
|
47
|
+
returnToApplication: string;
|
|
48
|
+
};
|
|
49
|
+
userCodeLength: number;
|
|
50
|
+
viewPaths: {
|
|
51
|
+
auth: {
|
|
52
|
+
deviceAuthorization: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
} & {
|
|
56
|
+
id: "deviceAuthorization";
|
|
57
|
+
}) & {
|
|
58
|
+
id: "deviceAuthorization";
|
|
59
|
+
};
|
package/dist/plugins.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export * from './plugins/api-key/api-key-query-keys';
|
|
|
5
5
|
export * from './plugins/delete-user/delete-user-localization';
|
|
6
6
|
export * from './plugins/delete-user/delete-user-mutation-keys';
|
|
7
7
|
export * from './plugins/delete-user/delete-user-plugin';
|
|
8
|
+
export * from './plugins/device-authorization/device-authorization-localization';
|
|
9
|
+
export * from './plugins/device-authorization/device-authorization-mutation-keys';
|
|
10
|
+
export * from './plugins/device-authorization/device-authorization-plugin';
|
|
8
11
|
export * from './plugins/last-login-method/last-login-method-localization';
|
|
9
12
|
export * from './plugins/last-login-method/last-login-method-plugin';
|
|
10
13
|
export * from './plugins/magic-link/magic-link-localization';
|
package/dist/plugins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./create-auth-plugin-
|
|
1
|
+
import { n as e, r as t, t as n } from "./create-auth-plugin-D4wsYnHD.js";
|
|
2
2
|
//#region src/plugins/api-key/api-key-localization.ts
|
|
3
3
|
var r = {
|
|
4
4
|
apiKey: "API key",
|
|
@@ -46,35 +46,79 @@ var r = {
|
|
|
46
46
|
},
|
|
47
47
|
sendDeleteAccountVerification: e.sendDeleteAccountVerification ?? !1
|
|
48
48
|
})), u = {
|
|
49
|
+
deviceAuthorization: "Device Authorization",
|
|
50
|
+
deviceAuthorizationDescription: "Enter the code displayed on your device.",
|
|
51
|
+
deviceCode: "Device code",
|
|
52
|
+
invalidDeviceCode: "The code is invalid or has expired.",
|
|
53
|
+
continue: "Continue",
|
|
54
|
+
approveDevice: "Approve Device",
|
|
55
|
+
approveDeviceDescription: "A device is requesting access to your account.",
|
|
56
|
+
signedInAs: "Signed in as",
|
|
57
|
+
approve: "Approve",
|
|
58
|
+
deny: "Deny",
|
|
59
|
+
deviceApproved: "Device Approved",
|
|
60
|
+
deviceApprovedDescription: "The device can now access your account. You can return to it and continue.",
|
|
61
|
+
deviceDenied: "Device Denied",
|
|
62
|
+
deviceDeniedDescription: "The device was not granted access to your account.",
|
|
63
|
+
returnToApplication: "Return to application"
|
|
64
|
+
}, d = {
|
|
65
|
+
all: ["auth", "deviceAuthorization"],
|
|
66
|
+
verify: [
|
|
67
|
+
"auth",
|
|
68
|
+
"deviceAuthorization",
|
|
69
|
+
"verify"
|
|
70
|
+
],
|
|
71
|
+
approve: [
|
|
72
|
+
"auth",
|
|
73
|
+
"deviceAuthorization",
|
|
74
|
+
"approve"
|
|
75
|
+
],
|
|
76
|
+
deny: [
|
|
77
|
+
"auth",
|
|
78
|
+
"deviceAuthorization",
|
|
79
|
+
"deny"
|
|
80
|
+
]
|
|
81
|
+
}, f = 8;
|
|
82
|
+
function p(e) {
|
|
83
|
+
return e === void 0 || !Number.isFinite(e) ? f : Math.max(1, Math.floor(e));
|
|
84
|
+
}
|
|
85
|
+
var m = n("deviceAuthorization", (e = {}) => ({
|
|
86
|
+
localization: {
|
|
87
|
+
...u,
|
|
88
|
+
...e.localization
|
|
89
|
+
},
|
|
90
|
+
userCodeLength: p(e.userCodeLength),
|
|
91
|
+
viewPaths: { auth: { deviceAuthorization: e.path ?? "device" } }
|
|
92
|
+
})), h = {
|
|
49
93
|
lastUsed: "Last used",
|
|
50
94
|
lastUsedShort: "Last"
|
|
51
|
-
},
|
|
52
|
-
...
|
|
95
|
+
}, g = n("lastLoginMethod", (e = {}) => ({ localization: {
|
|
96
|
+
...h,
|
|
53
97
|
...e.localization
|
|
54
|
-
} })),
|
|
98
|
+
} })), _ = {
|
|
55
99
|
magicLink: "Magic Link",
|
|
56
100
|
sendMagicLink: "Send Magic Link",
|
|
57
101
|
magicLinkSent: "Check your email for the magic link",
|
|
58
102
|
magicLinkSentTo: "We sent a magic link to {{email}}"
|
|
59
|
-
},
|
|
103
|
+
}, v = { signIn: [
|
|
60
104
|
"auth",
|
|
61
105
|
"signIn",
|
|
62
106
|
"magicLink"
|
|
63
|
-
] },
|
|
107
|
+
] }, y = n("magicLink", (e = {}) => ({
|
|
64
108
|
localization: {
|
|
65
|
-
...
|
|
109
|
+
..._,
|
|
66
110
|
...e.localization
|
|
67
111
|
},
|
|
68
112
|
viewPaths: { auth: {
|
|
69
113
|
magicLink: e.path ?? "magic-link",
|
|
70
114
|
magicLinkSent: e.sentPath ?? "magic-link-sent"
|
|
71
115
|
} }
|
|
72
|
-
})),
|
|
116
|
+
})), b = {
|
|
73
117
|
switchAccount: "Switch Account",
|
|
74
118
|
addAccount: "Add Account",
|
|
75
119
|
manageAccounts: "Manage accounts",
|
|
76
120
|
manageAccountsDescription: "Manage your accounts for secure access."
|
|
77
|
-
},
|
|
121
|
+
}, x = {
|
|
78
122
|
all: ["auth", "multiSession"],
|
|
79
123
|
revoke: [
|
|
80
124
|
"auth",
|
|
@@ -86,14 +130,14 @@ var r = {
|
|
|
86
130
|
"multiSession",
|
|
87
131
|
"setActive"
|
|
88
132
|
]
|
|
89
|
-
},
|
|
90
|
-
...
|
|
133
|
+
}, S = n("multiSession", (e = {}) => ({ localization: {
|
|
134
|
+
...b,
|
|
91
135
|
...e.localization
|
|
92
|
-
} })),
|
|
136
|
+
} })), C = {
|
|
93
137
|
all: (t) => [...e.user(t), "multiSession"],
|
|
94
|
-
lists: (e) => [...
|
|
95
|
-
list: (e, t) => [...
|
|
96
|
-
},
|
|
138
|
+
lists: (e) => [...C.all(e), "list"],
|
|
139
|
+
list: (e, t) => [...C.lists(e), t ?? null]
|
|
140
|
+
}, w = {
|
|
97
141
|
accept: "Accept",
|
|
98
142
|
accepted: "Accepted",
|
|
99
143
|
actions: "Actions",
|
|
@@ -150,7 +194,7 @@ var r = {
|
|
|
150
194
|
status: "Status",
|
|
151
195
|
uploadLogo: "Upload logo",
|
|
152
196
|
userInvitationsEmptyDescription: "Invitations to join an organization will show up here."
|
|
153
|
-
},
|
|
197
|
+
}, T = {
|
|
154
198
|
all: ["auth", "organization"],
|
|
155
199
|
create: [
|
|
156
200
|
"auth",
|
|
@@ -212,9 +256,9 @@ var r = {
|
|
|
212
256
|
"organization",
|
|
213
257
|
"checkSlug"
|
|
214
258
|
]
|
|
215
|
-
},
|
|
259
|
+
}, E = n("organization", (e = {}) => {
|
|
216
260
|
let n = {
|
|
217
|
-
...
|
|
261
|
+
...w,
|
|
218
262
|
...e.localization
|
|
219
263
|
};
|
|
220
264
|
return {
|
|
@@ -242,38 +286,38 @@ var r = {
|
|
|
242
286
|
}
|
|
243
287
|
}
|
|
244
288
|
};
|
|
245
|
-
}),
|
|
289
|
+
}), D = {
|
|
246
290
|
all: (t) => [...e.user(t), "organization"],
|
|
247
|
-
lists: (e) => [...
|
|
248
|
-
list: (e, t) => [...
|
|
249
|
-
fullDetails: (e) => [...
|
|
250
|
-
fullDetail: (e, t) => [...
|
|
251
|
-
activeOrganizations: (e) => [...
|
|
252
|
-
activeOrganization: (e, t) => [...
|
|
291
|
+
lists: (e) => [...D.all(e), "list"],
|
|
292
|
+
list: (e, t) => [...D.lists(e), t ?? null],
|
|
293
|
+
fullDetails: (e) => [...D.all(e), "fullDetails"],
|
|
294
|
+
fullDetail: (e, t) => [...D.fullDetails(e), t ?? null],
|
|
295
|
+
activeOrganizations: (e) => [...D.all(e), "active"],
|
|
296
|
+
activeOrganization: (e, t) => [...D.activeOrganizations(e), t ?? null],
|
|
253
297
|
members: {
|
|
254
|
-
all: (e) => [...
|
|
255
|
-
lists: (e) => [...
|
|
256
|
-
list: (e, t) => [...
|
|
298
|
+
all: (e) => [...D.all(e), "members"],
|
|
299
|
+
lists: (e) => [...D.members.all(e), "list"],
|
|
300
|
+
list: (e, t) => [...D.members.lists(e), t ?? null]
|
|
257
301
|
},
|
|
258
302
|
invitations: {
|
|
259
|
-
all: (e) => [...
|
|
260
|
-
lists: (e) => [...
|
|
261
|
-
list: (e, t) => [...
|
|
303
|
+
all: (e) => [...D.all(e), "invitations"],
|
|
304
|
+
lists: (e) => [...D.invitations.all(e), "list"],
|
|
305
|
+
list: (e, t) => [...D.invitations.lists(e), t ?? null]
|
|
262
306
|
},
|
|
263
307
|
userInvitations: {
|
|
264
|
-
all: (e) => [...
|
|
265
|
-
lists: (e) => [...
|
|
266
|
-
list: (e, t) => [...
|
|
308
|
+
all: (e) => [...D.all(e), "userInvitations"],
|
|
309
|
+
lists: (e) => [...D.userInvitations.all(e), "list"],
|
|
310
|
+
list: (e, t) => [...D.userInvitations.lists(e), t ?? null]
|
|
267
311
|
},
|
|
268
312
|
permissions: {
|
|
269
|
-
all: (e) => [...
|
|
313
|
+
all: (e) => [...D.all(e), "permissions"],
|
|
270
314
|
has: (e, t) => [
|
|
271
|
-
...
|
|
315
|
+
...D.permissions.all(e),
|
|
272
316
|
"has",
|
|
273
317
|
t ?? null
|
|
274
318
|
]
|
|
275
319
|
}
|
|
276
|
-
},
|
|
320
|
+
}, O = {
|
|
277
321
|
passkey: "Passkey",
|
|
278
322
|
addPasskey: "Add passkey",
|
|
279
323
|
deletePasskey: "Delete passkey {{name}}",
|
|
@@ -283,7 +327,7 @@ var r = {
|
|
|
283
327
|
passkeysDescription: "Create a passkey to securely access your account.",
|
|
284
328
|
noPasskeys: "No passkeys",
|
|
285
329
|
name: "Name"
|
|
286
|
-
},
|
|
330
|
+
}, k = {
|
|
287
331
|
signIn: [
|
|
288
332
|
"auth",
|
|
289
333
|
"signIn",
|
|
@@ -299,22 +343,22 @@ var r = {
|
|
|
299
343
|
"passkey",
|
|
300
344
|
"deletePasskey"
|
|
301
345
|
]
|
|
302
|
-
},
|
|
303
|
-
...
|
|
346
|
+
}, A = n("passkey", (e = {}) => ({ localization: {
|
|
347
|
+
...O,
|
|
304
348
|
...e.localization
|
|
305
|
-
} })),
|
|
349
|
+
} })), j = {
|
|
306
350
|
all: (t) => [...e.user(t), "passkey"],
|
|
307
|
-
lists: (e) => [...
|
|
308
|
-
list: (e, t) => [...
|
|
309
|
-
},
|
|
351
|
+
lists: (e) => [...j.all(e), "list"],
|
|
352
|
+
list: (e, t) => [...j.lists(e), t ?? null]
|
|
353
|
+
}, M = {
|
|
310
354
|
appearance: "Appearance",
|
|
311
355
|
theme: "Theme",
|
|
312
356
|
system: "System",
|
|
313
357
|
light: "Light",
|
|
314
358
|
dark: "Dark"
|
|
315
|
-
},
|
|
359
|
+
}, N = n("theme", (e) => ({
|
|
316
360
|
localization: {
|
|
317
|
-
...
|
|
361
|
+
...M,
|
|
318
362
|
...e.localization
|
|
319
363
|
},
|
|
320
364
|
theme: e.theme ?? "system",
|
|
@@ -324,7 +368,7 @@ var r = {
|
|
|
324
368
|
"light",
|
|
325
369
|
"dark"
|
|
326
370
|
]
|
|
327
|
-
})),
|
|
371
|
+
})), P = {
|
|
328
372
|
username: "Username",
|
|
329
373
|
usernamePlaceholder: "Username",
|
|
330
374
|
usernameOrEmailPlaceholder: "Username or email",
|
|
@@ -332,16 +376,16 @@ var r = {
|
|
|
332
376
|
usernameTaken: "Username is already taken. Please try another.",
|
|
333
377
|
displayUsername: "Display Username",
|
|
334
378
|
displayUsernamePlaceholder: "Display Username"
|
|
335
|
-
},
|
|
379
|
+
}, F = {
|
|
336
380
|
signIn: [
|
|
337
381
|
"auth",
|
|
338
382
|
"signIn",
|
|
339
383
|
"username"
|
|
340
384
|
],
|
|
341
385
|
isUsernameAvailable: ["auth", "isUsernameAvailable"]
|
|
342
|
-
},
|
|
386
|
+
}, I = n("username", (e = {}) => {
|
|
343
387
|
let t = e.minUsernameLength ?? 3, n = e.maxUsernameLength ?? 30, r = {
|
|
344
|
-
...
|
|
388
|
+
...P,
|
|
345
389
|
...e.localization
|
|
346
390
|
};
|
|
347
391
|
return {
|
|
@@ -369,4 +413,4 @@ var r = {
|
|
|
369
413
|
};
|
|
370
414
|
});
|
|
371
415
|
//#endregion
|
|
372
|
-
export { r as apiKeyLocalization, i as apiKeyMutationKeys, a as apiKeyPlugin, o as apiKeyQueryKeys, s as deleteUserLocalization, c as deleteUserMutationKeys, l as deleteUserPlugin, u as
|
|
416
|
+
export { r as apiKeyLocalization, i as apiKeyMutationKeys, a as apiKeyPlugin, o as apiKeyQueryKeys, s as deleteUserLocalization, c as deleteUserMutationKeys, l as deleteUserPlugin, u as deviceAuthorizationLocalization, d as deviceAuthorizationMutationKeys, m as deviceAuthorizationPlugin, h as lastLoginMethodLocalization, g as lastLoginMethodPlugin, _ as magicLinkLocalization, v as magicLinkMutationKeys, y as magicLinkPlugin, b as multiSessionLocalization, x as multiSessionMutationKeys, S as multiSessionPlugin, C as multiSessionQueryKeys, w as organizationLocalization, T as organizationMutationKeys, E as organizationPlugin, D as organizationQueryKeys, O as passkeyLocalization, k as passkeyMutationKeys, A as passkeyPlugin, j as passkeyQueryKeys, M as themeLocalization, N as themePlugin, P as usernameLocalization, F as usernameMutationKeys, I as usernamePlugin };
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./config"
|
|
|
2
2
|
export * from "./lib/auth-mutation-keys"
|
|
3
3
|
export * from "./lib/auth-plugin"
|
|
4
4
|
export * from "./lib/auth-query-keys"
|
|
5
|
+
export * from "./lib/auth-redirect"
|
|
5
6
|
export * from "./lib/base-paths"
|
|
6
7
|
export * from "./lib/create-auth-plugin"
|
|
7
8
|
export * from "./lib/deep-partial"
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
* package the mutation options factory came from.
|
|
20
20
|
*
|
|
21
21
|
* Plugin-specific mutation keys live alongside their plugin (e.g.
|
|
22
|
-
* `apiKeyMutationKeys`, `
|
|
23
|
-
* `
|
|
24
|
-
* `deleteUserMutationKeys`).
|
|
25
|
-
*
|
|
22
|
+
* `apiKeyMutationKeys`, `deviceAuthorizationMutationKeys`,
|
|
23
|
+
* `organizationMutationKeys`, `passkeyMutationKeys`, `magicLinkMutationKeys`,
|
|
24
|
+
* `multiSessionMutationKeys`, `usernameMutationKeys`, `deleteUserMutationKeys`).
|
|
25
|
+
* Plugin-contributed sign-in strategies stay under the shared
|
|
26
|
+
* `["auth", "signIn", ...]` namespace so
|
|
26
27
|
* `useIsMutating({ mutationKey: authMutationKeys.signIn.all })` still
|
|
27
28
|
* matches them.
|
|
28
29
|
*
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const ABSOLUTE_HTTP_URL = /^https?:\/\//i
|
|
2
|
+
|
|
3
|
+
function hasUnsafeRedirectCharacters(value: string): boolean {
|
|
4
|
+
for (const character of value) {
|
|
5
|
+
const codePoint = character.codePointAt(0)
|
|
6
|
+
if (
|
|
7
|
+
character === "\\" ||
|
|
8
|
+
codePoint === undefined ||
|
|
9
|
+
codePoint <= 31 ||
|
|
10
|
+
codePoint === 127
|
|
11
|
+
) {
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Normalize a redirect target to a same-origin path.
|
|
21
|
+
*
|
|
22
|
+
* Root-relative paths and same-origin HTTP(S) URLs are accepted. Invalid,
|
|
23
|
+
* cross-origin, protocol-relative, and non-HTTP targets fall back to `/`.
|
|
24
|
+
*
|
|
25
|
+
* @param redirectTo - Requested redirect target
|
|
26
|
+
* @param origin - Origin used to validate and normalize the target
|
|
27
|
+
* @returns A same-origin path including its query string and hash
|
|
28
|
+
*/
|
|
29
|
+
export function getSafeRedirectTo(
|
|
30
|
+
redirectTo: string | null | undefined,
|
|
31
|
+
origin: string
|
|
32
|
+
): string {
|
|
33
|
+
if (!redirectTo || hasUnsafeRedirectCharacters(redirectTo)) return "/"
|
|
34
|
+
|
|
35
|
+
const target = redirectTo.trim()
|
|
36
|
+
if (
|
|
37
|
+
!target ||
|
|
38
|
+
target.startsWith("//") ||
|
|
39
|
+
(!target.startsWith("/") && !ABSOLUTE_HTTP_URL.test(target))
|
|
40
|
+
) {
|
|
41
|
+
return "/"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const baseURL = new URL(origin)
|
|
46
|
+
const targetURL = new URL(target, baseURL)
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
targetURL.origin !== baseURL.origin ||
|
|
50
|
+
targetURL.username ||
|
|
51
|
+
targetURL.password
|
|
52
|
+
) {
|
|
53
|
+
return "/"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `${targetURL.pathname}${targetURL.search}${targetURL.hash}`
|
|
57
|
+
} catch {
|
|
58
|
+
return "/"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type AuthRedirectAction =
|
|
63
|
+
| { type: "redirect"; to: string }
|
|
64
|
+
| { type: "signIn"; to: string }
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the next action for the authenticated redirect view.
|
|
68
|
+
*
|
|
69
|
+
* Signed-in users continue to the validated target. Signed-out users are sent
|
|
70
|
+
* to sign in with the current redirect-view URL preserved, allowing the view
|
|
71
|
+
* to perform a full-page redirect after authentication.
|
|
72
|
+
*
|
|
73
|
+
* @param currentURL - Current redirect-view URL
|
|
74
|
+
* @param authenticated - Whether the current user has a session
|
|
75
|
+
* @param signInPath - Same-origin path to the sign-in view
|
|
76
|
+
* @returns The redirect or sign-in action to perform
|
|
77
|
+
*/
|
|
78
|
+
export function getAuthRedirectAction(
|
|
79
|
+
currentURL: URL,
|
|
80
|
+
authenticated: boolean,
|
|
81
|
+
signInPath: string
|
|
82
|
+
): AuthRedirectAction {
|
|
83
|
+
const requestedTarget = getSafeRedirectTo(
|
|
84
|
+
currentURL.searchParams.get("redirectTo"),
|
|
85
|
+
currentURL.origin
|
|
86
|
+
)
|
|
87
|
+
const targetURL = new URL(requestedTarget, currentURL.origin)
|
|
88
|
+
const redirectTo =
|
|
89
|
+
targetURL.pathname === currentURL.pathname ? "/" : requestedTarget
|
|
90
|
+
|
|
91
|
+
if (authenticated) {
|
|
92
|
+
return { type: "redirect", to: redirectTo }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const signInURL = new URL(signInPath, currentURL.origin)
|
|
96
|
+
signInURL.searchParams.set(
|
|
97
|
+
"redirectTo",
|
|
98
|
+
`${currentURL.pathname}${currentURL.search}${currentURL.hash}`
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
type: "signIn",
|
|
103
|
+
to: `${signInURL.pathname}${signInURL.search}${signInURL.hash}`
|
|
104
|
+
}
|
|
105
|
+
}
|
package/src/lib/view-paths.ts
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* View path segments for authentication routes.
|
|
3
3
|
*/
|
|
4
4
|
export interface AuthViewPaths {
|
|
5
|
+
/**
|
|
6
|
+
* Path segment for the authenticated redirect view
|
|
7
|
+
* @default "redirect"
|
|
8
|
+
*/
|
|
9
|
+
redirect: string
|
|
5
10
|
/**
|
|
6
11
|
* Path segment for the sign-in view
|
|
7
12
|
* @default "sign-in"
|
|
@@ -67,6 +72,7 @@ export type ViewPaths = {
|
|
|
67
72
|
|
|
68
73
|
export const viewPaths: ViewPaths = {
|
|
69
74
|
auth: {
|
|
75
|
+
redirect: "redirect",
|
|
70
76
|
signIn: "sign-in",
|
|
71
77
|
signUp: "sign-up",
|
|
72
78
|
forgotPassword: "forgot-password",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const deviceAuthorizationLocalization = {
|
|
2
|
+
/** @remarks `"Device Authorization"` */
|
|
3
|
+
deviceAuthorization: "Device Authorization",
|
|
4
|
+
/** @remarks `"Enter the code displayed on your device."` */
|
|
5
|
+
deviceAuthorizationDescription: "Enter the code displayed on your device.",
|
|
6
|
+
/** @remarks `"Device code"` */
|
|
7
|
+
deviceCode: "Device code",
|
|
8
|
+
/** @remarks `"The code is invalid or has expired."` */
|
|
9
|
+
invalidDeviceCode: "The code is invalid or has expired.",
|
|
10
|
+
/** @remarks `"Continue"` */
|
|
11
|
+
continue: "Continue",
|
|
12
|
+
/** @remarks `"Approve Device"` */
|
|
13
|
+
approveDevice: "Approve Device",
|
|
14
|
+
/** @remarks `"A device is requesting access to your account."` */
|
|
15
|
+
approveDeviceDescription: "A device is requesting access to your account.",
|
|
16
|
+
/** @remarks `"Signed in as"` */
|
|
17
|
+
signedInAs: "Signed in as",
|
|
18
|
+
/** @remarks `"Approve"` */
|
|
19
|
+
approve: "Approve",
|
|
20
|
+
/** @remarks `"Deny"` */
|
|
21
|
+
deny: "Deny",
|
|
22
|
+
/** @remarks `"Device Approved"` */
|
|
23
|
+
deviceApproved: "Device Approved",
|
|
24
|
+
/** @remarks `"The device can now access your account. You can return to it and continue."` */
|
|
25
|
+
deviceApprovedDescription:
|
|
26
|
+
"The device can now access your account. You can return to it and continue.",
|
|
27
|
+
/** @remarks `"Device Denied"` */
|
|
28
|
+
deviceDenied: "Device Denied",
|
|
29
|
+
/** @remarks `"The device was not granted access to your account."` */
|
|
30
|
+
deviceDeniedDescription: "The device was not granted access to your account.",
|
|
31
|
+
/** @remarks `"Return to application"` */
|
|
32
|
+
returnToApplication: "Return to application"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type DeviceAuthorizationLocalization =
|
|
36
|
+
typeof deviceAuthorizationLocalization
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mutation keys contributed by the device-authorization plugin.
|
|
3
|
+
*
|
|
4
|
+
* All keys live under the shared `["auth"]` namespace so global auth
|
|
5
|
+
* mutation observers handle errors consistently.
|
|
6
|
+
*/
|
|
7
|
+
export const deviceAuthorizationMutationKeys = {
|
|
8
|
+
/** Prefix matching every device-authorization mutation. */
|
|
9
|
+
all: ["auth", "deviceAuthorization"] as const,
|
|
10
|
+
/** Key for verifying and claiming a user code. */
|
|
11
|
+
verify: ["auth", "deviceAuthorization", "verify"] as const,
|
|
12
|
+
/** Key for approving a pending device request. */
|
|
13
|
+
approve: ["auth", "deviceAuthorization", "approve"] as const,
|
|
14
|
+
/** Key for denying a pending device request. */
|
|
15
|
+
deny: ["auth", "deviceAuthorization", "deny"] as const
|
|
16
|
+
} as const
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createAuthPlugin } from "../../lib/create-auth-plugin"
|
|
2
|
+
// Side-effect import so this file participates in declaration merging on the
|
|
3
|
+
// same module instance that external consumers reach via `@better-auth-ui/core`.
|
|
4
|
+
import type {} from "../../lib/view-paths"
|
|
5
|
+
import {
|
|
6
|
+
type DeviceAuthorizationLocalization,
|
|
7
|
+
deviceAuthorizationLocalization
|
|
8
|
+
} from "./device-authorization-localization"
|
|
9
|
+
|
|
10
|
+
declare module "../../lib/view-paths" {
|
|
11
|
+
/** Widens `AuthViewPaths` with the device-authorization path when this plugin is imported. */
|
|
12
|
+
interface AuthViewPaths {
|
|
13
|
+
/** @default "device" */
|
|
14
|
+
deviceAuthorization?: string
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type DeviceAuthorizationPluginOptions = {
|
|
19
|
+
/**
|
|
20
|
+
* Override the plugin's default localization strings.
|
|
21
|
+
* @remarks `DeviceAuthorizationLocalization`
|
|
22
|
+
*/
|
|
23
|
+
localization?: Partial<DeviceAuthorizationLocalization>
|
|
24
|
+
/**
|
|
25
|
+
* URL segment for the device-authorization view.
|
|
26
|
+
* @remarks `string`
|
|
27
|
+
* @default "device"
|
|
28
|
+
*/
|
|
29
|
+
path?: string
|
|
30
|
+
/**
|
|
31
|
+
* Number of characters rendered by the device-code input.
|
|
32
|
+
*
|
|
33
|
+
* Keep this in sync with Better Auth's `deviceAuthorization({ userCodeLength })`
|
|
34
|
+
* server option.
|
|
35
|
+
*
|
|
36
|
+
* @default 8
|
|
37
|
+
*/
|
|
38
|
+
userCodeLength?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEFAULT_USER_CODE_LENGTH = 8
|
|
42
|
+
|
|
43
|
+
function resolveUserCodeLength(value?: number) {
|
|
44
|
+
if (value === undefined || !Number.isFinite(value)) {
|
|
45
|
+
return DEFAULT_USER_CODE_LENGTH
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return Math.max(1, Math.floor(value))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const deviceAuthorizationPlugin = createAuthPlugin(
|
|
52
|
+
"deviceAuthorization",
|
|
53
|
+
(options: DeviceAuthorizationPluginOptions = {}) => ({
|
|
54
|
+
localization: {
|
|
55
|
+
...deviceAuthorizationLocalization,
|
|
56
|
+
...options.localization
|
|
57
|
+
},
|
|
58
|
+
userCodeLength: resolveUserCodeLength(options.userCodeLength),
|
|
59
|
+
viewPaths: {
|
|
60
|
+
auth: {
|
|
61
|
+
deviceAuthorization: options.path ?? "device"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
)
|
package/src/plugins.ts
CHANGED
|
@@ -5,6 +5,9 @@ export * from "./plugins/api-key/api-key-query-keys"
|
|
|
5
5
|
export * from "./plugins/delete-user/delete-user-localization"
|
|
6
6
|
export * from "./plugins/delete-user/delete-user-mutation-keys"
|
|
7
7
|
export * from "./plugins/delete-user/delete-user-plugin"
|
|
8
|
+
export * from "./plugins/device-authorization/device-authorization-localization"
|
|
9
|
+
export * from "./plugins/device-authorization/device-authorization-mutation-keys"
|
|
10
|
+
export * from "./plugins/device-authorization/device-authorization-plugin"
|
|
8
11
|
export * from "./plugins/last-login-method/last-login-method-localization"
|
|
9
12
|
export * from "./plugins/last-login-method/last-login-method-plugin"
|
|
10
13
|
export * from "./plugins/magic-link/magic-link-localization"
|