@delmaredigital/payload-better-auth 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/components/LoginView.d.ts +1 -2
- package/dist/components/LoginView.js +24 -10
- package/dist/components/PasskeyRegisterButton.d.ts +1 -2
- package/dist/components/PasskeyRegisterButton.js +18 -3
- package/dist/components/PasskeySignInButton.d.ts +1 -2
- package/dist/components/PasskeySignInButton.js +18 -3
- package/dist/components/management/ApiKeysManagementClient.js +12 -5
- package/dist/components/management/PasskeysManagementClient.d.ts +1 -2
- package/dist/components/management/PasskeysManagementClient.js +20 -6
- package/dist/components/management/TwoFactorManagementClient.d.ts +1 -2
- package/dist/exports/client.d.ts +125 -961
- package/dist/exports/client.js +20 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Better Auth adapter and plugins for Payload CMS. Enables seamless integration be
|
|
|
12
12
|
<a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdelmaredigital%2Fdd-starter&project-name=my-payload-site&build-command=pnpm%20run%20ci&env=PAYLOAD_SECRET,BETTER_AUTH_SECRET&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22productSlug%22%3A%22neon%22%2C%22integrationSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22blob%22%7D%5D"><img src="https://vercel.com/button" alt="Deploy with Vercel" height="32"></a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
+
> **Upgrading to 0.5?** This release requires Better Auth 1.5 and includes breaking changes to client plugins, API key imports, and auth instance types. See the [CHANGELOG](./CHANGELOG.md#053---2026-03-01) for migration instructions.
|
|
16
|
+
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
## Documentation
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type PayloadAuthClient } from '../exports/client.js';
|
|
2
1
|
export type LoginViewProps = {
|
|
3
2
|
/** Optional pre-configured auth client */
|
|
4
|
-
authClient?:
|
|
3
|
+
authClient?: any;
|
|
5
4
|
/** Custom logo element */
|
|
6
5
|
logo?: React.ReactNode;
|
|
7
6
|
/** Login page title. Default: 'Login' */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
3
|
+
import { useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { useRouter } from 'next/navigation.js';
|
|
5
|
-
import {
|
|
5
|
+
import { createAuthClient } from 'better-auth/react';
|
|
6
|
+
import { twoFactorClient } from 'better-auth/client/plugins';
|
|
6
7
|
import { hasAnyRole, hasAllRoles } from '../utils/access.js';
|
|
7
8
|
/**
|
|
8
9
|
* Check if user has the required role(s)
|
|
@@ -42,12 +43,25 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
42
43
|
// Two-factor authentication state
|
|
43
44
|
const [totpCode, setTotpCode] = useState('');
|
|
44
45
|
const [totpLoading, setTotpLoading] = useState(false);
|
|
45
|
-
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const clientRef = useRef(null);
|
|
48
|
+
const getClient = async ()=>{
|
|
49
|
+
if (providedClient) return providedClient;
|
|
50
|
+
if (clientRef.current) return clientRef.current;
|
|
51
|
+
const { passkeyClient } = await import('@better-auth/passkey/client');
|
|
52
|
+
clientRef.current = createAuthClient({
|
|
53
|
+
plugins: [
|
|
54
|
+
twoFactorClient(),
|
|
55
|
+
passkeyClient()
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
return clientRef.current;
|
|
59
|
+
};
|
|
46
60
|
// Check if user is already logged in on mount
|
|
47
61
|
useEffect(()=>{
|
|
48
62
|
async function checkSession() {
|
|
49
63
|
try {
|
|
50
|
-
const client = getClient();
|
|
64
|
+
const client = await getClient();
|
|
51
65
|
const result = await client.getSession();
|
|
52
66
|
if (result.data?.user) {
|
|
53
67
|
const user = result.data.user;
|
|
@@ -140,7 +154,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
140
154
|
setSuccessMessage(null);
|
|
141
155
|
setAccessDenied(false);
|
|
142
156
|
try {
|
|
143
|
-
const client = getClient();
|
|
157
|
+
const client = await getClient();
|
|
144
158
|
const result = await client.signIn.email({
|
|
145
159
|
email,
|
|
146
160
|
password
|
|
@@ -190,7 +204,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
190
204
|
return;
|
|
191
205
|
}
|
|
192
206
|
try {
|
|
193
|
-
const client = getClient();
|
|
207
|
+
const client = await getClient();
|
|
194
208
|
const result = await client.signUp.email({
|
|
195
209
|
email,
|
|
196
210
|
password,
|
|
@@ -237,7 +251,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
237
251
|
setError(null);
|
|
238
252
|
setSuccessMessage(null);
|
|
239
253
|
try {
|
|
240
|
-
const client = getClient();
|
|
254
|
+
const client = await getClient();
|
|
241
255
|
const result = await client.requestPasswordReset({
|
|
242
256
|
email,
|
|
243
257
|
redirectTo: resetPasswordUrl ?? `${window.location.origin}/admin/reset-password`
|
|
@@ -260,7 +274,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
260
274
|
setTotpLoading(true);
|
|
261
275
|
setError(null);
|
|
262
276
|
try {
|
|
263
|
-
const client = getClient();
|
|
277
|
+
const client = await getClient();
|
|
264
278
|
const result = await client.twoFactor.verifyTotp({
|
|
265
279
|
code: totpCode
|
|
266
280
|
});
|
|
@@ -313,7 +327,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
313
327
|
setError(null);
|
|
314
328
|
setAccessDenied(false);
|
|
315
329
|
try {
|
|
316
|
-
const client = getClient();
|
|
330
|
+
const client = await getClient();
|
|
317
331
|
const result = await client.signIn.passkey();
|
|
318
332
|
if (result.error) {
|
|
319
333
|
setError(result.error.message ?? 'Passkey authentication failed');
|
|
@@ -348,7 +362,7 @@ export function LoginView({ authClient: providedClient, logo, title = 'Login', a
|
|
|
348
362
|
}
|
|
349
363
|
}
|
|
350
364
|
async function handleSignOut() {
|
|
351
|
-
const client = getClient();
|
|
365
|
+
const client = await getClient();
|
|
352
366
|
await client.signOut();
|
|
353
367
|
setAccessDenied(false);
|
|
354
368
|
router.refresh();
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type ButtonHTMLAttributes } from 'react';
|
|
2
|
-
import { type PayloadAuthClient } from '../exports/client.js';
|
|
3
2
|
export type PasskeyRegisterButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> & {
|
|
4
3
|
/** Optional pre-configured auth client */
|
|
5
|
-
authClient?:
|
|
4
|
+
authClient?: any;
|
|
6
5
|
/** Optional name for the passkey */
|
|
7
6
|
passkeyName?: string;
|
|
8
7
|
/** Callback when registration succeeds */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { useState, useRef } from 'react';
|
|
4
|
+
import { createAuthClient } from 'better-auth/react';
|
|
5
|
+
import { twoFactorClient } from 'better-auth/client/plugins';
|
|
5
6
|
/**
|
|
6
7
|
* Standalone passkey registration button component.
|
|
7
8
|
* Handles the WebAuthn registration flow with Better Auth.
|
|
@@ -27,10 +28,24 @@ import { createPayloadAuthClient } from '../exports/client.js';
|
|
|
27
28
|
* ```
|
|
28
29
|
*/ export function PasskeyRegisterButton({ authClient: providedClient, passkeyName, onSuccess, onError, label = 'Add Passkey', loadingLabel = 'Registering...', disabled, children, ...buttonProps }) {
|
|
29
30
|
const [loading, setLoading] = useState(false);
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
const clientRef = useRef(null);
|
|
33
|
+
async function getClient() {
|
|
34
|
+
if (providedClient) return providedClient;
|
|
35
|
+
if (clientRef.current) return clientRef.current;
|
|
36
|
+
const { passkeyClient } = await import('@better-auth/passkey/client');
|
|
37
|
+
clientRef.current = createAuthClient({
|
|
38
|
+
plugins: [
|
|
39
|
+
twoFactorClient(),
|
|
40
|
+
passkeyClient()
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
return clientRef.current;
|
|
44
|
+
}
|
|
30
45
|
async function handleClick() {
|
|
31
46
|
setLoading(true);
|
|
32
47
|
try {
|
|
33
|
-
const client =
|
|
48
|
+
const client = await getClient();
|
|
34
49
|
const result = await client.passkey.addPasskey({
|
|
35
50
|
name: passkeyName
|
|
36
51
|
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type ButtonHTMLAttributes } from 'react';
|
|
2
|
-
import { type PayloadAuthClient } from '../exports/client.js';
|
|
3
2
|
export type PasskeySignInButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> & {
|
|
4
3
|
/** Optional pre-configured auth client */
|
|
5
|
-
authClient?:
|
|
4
|
+
authClient?: any;
|
|
6
5
|
/** Callback when sign-in succeeds */
|
|
7
6
|
onSuccess?: (user: {
|
|
8
7
|
id: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { useState, useRef } from 'react';
|
|
4
|
+
import { createAuthClient } from 'better-auth/react';
|
|
5
|
+
import { twoFactorClient } from 'better-auth/client/plugins';
|
|
5
6
|
/**
|
|
6
7
|
* Standalone passkey sign-in button component.
|
|
7
8
|
* Handles the WebAuthn authentication flow with Better Auth.
|
|
@@ -25,10 +26,24 @@ import { createPayloadAuthClient } from '../exports/client.js';
|
|
|
25
26
|
* ```
|
|
26
27
|
*/ export function PasskeySignInButton({ authClient: providedClient, onSuccess, onError, label = 'Sign in with Passkey', loadingLabel = 'Authenticating...', disabled, children, ...buttonProps }) {
|
|
27
28
|
const [loading, setLoading] = useState(false);
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const clientRef = useRef(null);
|
|
31
|
+
async function getClient() {
|
|
32
|
+
if (providedClient) return providedClient;
|
|
33
|
+
if (clientRef.current) return clientRef.current;
|
|
34
|
+
const { passkeyClient } = await import('@better-auth/passkey/client');
|
|
35
|
+
clientRef.current = createAuthClient({
|
|
36
|
+
plugins: [
|
|
37
|
+
twoFactorClient(),
|
|
38
|
+
passkeyClient()
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
return clientRef.current;
|
|
42
|
+
}
|
|
28
43
|
async function handleClick() {
|
|
29
44
|
setLoading(true);
|
|
30
45
|
try {
|
|
31
|
-
const client =
|
|
46
|
+
const client = await getClient();
|
|
32
47
|
const result = await client.signIn.passkey();
|
|
33
48
|
if (result.error) {
|
|
34
49
|
onError?.(result.error.message ?? 'Passkey authentication failed');
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { createAuthClient } from 'better-auth/react';
|
|
5
|
-
import { apiKeyClient } from '@better-auth/api-key/client';
|
|
6
5
|
/**
|
|
7
6
|
* Group scopes by collection for the UI.
|
|
8
7
|
* Scopes like "posts:read", "posts:write" get grouped under "Posts"
|
|
@@ -90,11 +89,19 @@ import { apiKeyClient } from '@better-auth/api-key/client';
|
|
|
90
89
|
}, [
|
|
91
90
|
scopeGroups
|
|
92
91
|
]);
|
|
93
|
-
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
const clientRef = useRef(null);
|
|
94
|
+
const getClient = async ()=>{
|
|
95
|
+
if (providedClient) return providedClient;
|
|
96
|
+
if (clientRef.current) return clientRef.current;
|
|
97
|
+
const { apiKeyClient } = await import('@better-auth/api-key/client');
|
|
98
|
+
clientRef.current = createAuthClient({
|
|
94
99
|
plugins: [
|
|
95
100
|
apiKeyClient()
|
|
96
101
|
]
|
|
97
102
|
});
|
|
103
|
+
return clientRef.current;
|
|
104
|
+
};
|
|
98
105
|
// Toggle a scope selection
|
|
99
106
|
function toggleScope(scopeId) {
|
|
100
107
|
setSelectedScopes((prev)=>prev.includes(scopeId) ? prev.filter((s)=>s !== scopeId) : [
|
|
@@ -198,7 +205,7 @@ import { apiKeyClient } from '@better-auth/api-key/client';
|
|
|
198
205
|
setLoading(true);
|
|
199
206
|
setError(null);
|
|
200
207
|
try {
|
|
201
|
-
const client = getClient();
|
|
208
|
+
const client = await getClient();
|
|
202
209
|
const result = await client.apiKey.list();
|
|
203
210
|
if (result.error) {
|
|
204
211
|
setError(result.error.message ?? 'Failed to load API keys');
|
|
@@ -218,7 +225,7 @@ import { apiKeyClient } from '@better-auth/api-key/client';
|
|
|
218
225
|
setError(null);
|
|
219
226
|
setNewlyCreatedKey(null);
|
|
220
227
|
try {
|
|
221
|
-
const client = getClient();
|
|
228
|
+
const client = await getClient();
|
|
222
229
|
// Send scopes to server - server will convert to permissions
|
|
223
230
|
const createOptions = {
|
|
224
231
|
name: newKeyName
|
|
@@ -254,7 +261,7 @@ import { apiKeyClient } from '@better-auth/api-key/client';
|
|
|
254
261
|
setDeleting(keyId);
|
|
255
262
|
setError(null);
|
|
256
263
|
try {
|
|
257
|
-
const client = getClient();
|
|
264
|
+
const client = await getClient();
|
|
258
265
|
const result = await client.apiKey.delete({
|
|
259
266
|
keyId
|
|
260
267
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type PayloadAuthClient } from '../../exports/client.js';
|
|
2
1
|
export type PasskeysManagementClientProps = {
|
|
3
2
|
/** Optional pre-configured auth client */
|
|
4
|
-
authClient?:
|
|
3
|
+
authClient?: any;
|
|
5
4
|
/** Page title. Default: 'Passkeys' */
|
|
6
5
|
title?: string;
|
|
7
6
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
3
|
+
import { useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { Button, Banner } from '@payloadcms/ui';
|
|
5
5
|
import { PlusIcon } from '@payloadcms/ui/icons/Plus';
|
|
6
6
|
import { XIcon } from '@payloadcms/ui/icons/X';
|
|
7
|
-
import {
|
|
7
|
+
import { createAuthClient } from 'better-auth/react';
|
|
8
|
+
import { twoFactorClient } from 'better-auth/client/plugins';
|
|
8
9
|
/**
|
|
9
10
|
* Client component for passkey management.
|
|
10
11
|
* Lists, registers, and deletes passkeys.
|
|
@@ -17,7 +18,20 @@ import { createPayloadAuthClient } from '../../exports/client.js';
|
|
|
17
18
|
const [deleting, setDeleting] = useState(null);
|
|
18
19
|
const [showRegisterForm, setShowRegisterForm] = useState(false);
|
|
19
20
|
const [passkeyName, setPasskeyName] = useState('');
|
|
20
|
-
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const clientRef = useRef(null);
|
|
23
|
+
const getClient = async ()=>{
|
|
24
|
+
if (providedClient) return providedClient;
|
|
25
|
+
if (clientRef.current) return clientRef.current;
|
|
26
|
+
const { passkeyClient } = await import('@better-auth/passkey/client');
|
|
27
|
+
clientRef.current = createAuthClient({
|
|
28
|
+
plugins: [
|
|
29
|
+
twoFactorClient(),
|
|
30
|
+
passkeyClient()
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
return clientRef.current;
|
|
34
|
+
};
|
|
21
35
|
useEffect(()=>{
|
|
22
36
|
fetchPasskeys();
|
|
23
37
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -26,7 +40,7 @@ import { createPayloadAuthClient } from '../../exports/client.js';
|
|
|
26
40
|
setLoading(true);
|
|
27
41
|
setError(null);
|
|
28
42
|
try {
|
|
29
|
-
const client = getClient();
|
|
43
|
+
const client = await getClient();
|
|
30
44
|
const result = await client.passkey.listUserPasskeys();
|
|
31
45
|
if (result.error) {
|
|
32
46
|
setError(result.error.message ?? 'Failed to load passkeys');
|
|
@@ -44,7 +58,7 @@ import { createPayloadAuthClient } from '../../exports/client.js';
|
|
|
44
58
|
setError(null);
|
|
45
59
|
setSuccess(null);
|
|
46
60
|
try {
|
|
47
|
-
const client = getClient();
|
|
61
|
+
const client = await getClient();
|
|
48
62
|
const result = await client.passkey.addPasskey({
|
|
49
63
|
name: passkeyName || undefined
|
|
50
64
|
});
|
|
@@ -76,7 +90,7 @@ import { createPayloadAuthClient } from '../../exports/client.js';
|
|
|
76
90
|
setError(null);
|
|
77
91
|
setSuccess(null);
|
|
78
92
|
try {
|
|
79
|
-
const client = getClient();
|
|
93
|
+
const client = await getClient();
|
|
80
94
|
const result = await client.passkey.deletePasskey({
|
|
81
95
|
id: passkeyId
|
|
82
96
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type PayloadAuthClient } from '../../exports/client.js';
|
|
2
1
|
export type TwoFactorManagementClientProps = {
|
|
3
2
|
/** Optional pre-configured auth client */
|
|
4
|
-
authClient?:
|
|
3
|
+
authClient?: any;
|
|
5
4
|
/** Page title. Default: 'Two-Factor Authentication' */
|
|
6
5
|
title?: string;
|
|
7
6
|
/** Called after 2FA is enabled or disabled. Use to refresh form state. */
|