payload-better-auth 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -0
- package/dist/better-auth/crypto-shared.d.ts +61 -0
- package/dist/better-auth/crypto-shared.js +90 -0
- package/dist/better-auth/crypto-shared.js.map +1 -0
- package/dist/better-auth/databaseHooks.d.ts +5 -0
- package/dist/better-auth/databaseHooks.js +24 -0
- package/dist/better-auth/databaseHooks.js.map +1 -0
- package/dist/better-auth/plugin.d.ts +13 -0
- package/dist/better-auth/plugin.js +159 -0
- package/dist/better-auth/plugin.js.map +1 -0
- package/dist/better-auth/reconcile-queue.d.ts +70 -0
- package/dist/better-auth/reconcile-queue.js +311 -0
- package/dist/better-auth/reconcile-queue.js.map +1 -0
- package/dist/better-auth/sources.d.ts +35 -0
- package/dist/better-auth/sources.js +126 -0
- package/dist/better-auth/sources.js.map +1 -0
- package/dist/collections/Users/index.d.ts +5 -0
- package/dist/collections/Users/index.js +167 -0
- package/dist/collections/Users/index.js.map +1 -0
- package/dist/components/BeforeDashboardClient.d.ts +2 -0
- package/dist/components/BeforeDashboardClient.js +36 -0
- package/dist/components/BeforeDashboardClient.js.map +1 -0
- package/dist/components/BeforeDashboardServer.d.ts +3 -0
- package/dist/components/BeforeDashboardServer.js +22 -0
- package/dist/components/BeforeDashboardServer.js.map +1 -0
- package/dist/components/BeforeDashboardServer.module.css +5 -0
- package/dist/components/BetterAuthLoginServer.d.ts +5 -0
- package/dist/components/BetterAuthLoginServer.js +82 -0
- package/dist/components/BetterAuthLoginServer.js.map +1 -0
- package/dist/components/EmailPasswordFormClient.d.ts +5 -0
- package/dist/components/EmailPasswordFormClient.js +162 -0
- package/dist/components/EmailPasswordFormClient.js.map +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/payload/plugin.d.ts +7 -0
- package/dist/payload/plugin.js +76 -0
- package/dist/payload/plugin.js.map +1 -0
- package/dist/utils/payload-reconcile.d.ts +6 -0
- package/dist/utils/payload-reconcile.js +37 -0
- package/dist/utils/payload-reconcile.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createAuthClient } from 'better-auth/react';
|
|
2
|
+
import { APIError } from 'payload';
|
|
3
|
+
import { verifyCanonical } from '../../better-auth/crypto-shared.js';
|
|
4
|
+
const INTERNAL_SECRET = process.env.BA_TO_PAYLOAD_SECRET;
|
|
5
|
+
const authenticated = ({ req: { user } })=>{
|
|
6
|
+
return Boolean(user);
|
|
7
|
+
};
|
|
8
|
+
// (optional) simple anti-replay for Local API calls
|
|
9
|
+
const seenNonces = new Map();
|
|
10
|
+
const TTL = 5 * 60 * 1000;
|
|
11
|
+
setInterval(()=>{
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
for (const [k, exp] of seenNonces){
|
|
14
|
+
if (exp < now) {
|
|
15
|
+
seenNonces.delete(k);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}, 60_000).unref();
|
|
19
|
+
function basicSigOk(req) {
|
|
20
|
+
const sig = req.context.baSig;
|
|
21
|
+
const body = req.context.baBody;
|
|
22
|
+
if (!sig || !body) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const ok = verifyCanonical(body, sig, INTERNAL_SECRET);
|
|
26
|
+
if (!ok) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (seenNonces.has(sig.nonce)) {
|
|
30
|
+
return false;
|
|
31
|
+
} // replay
|
|
32
|
+
// don't mark used yet; final verification happens in hooks
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
export function createUsersCollection({ authClientOptions }) {
|
|
36
|
+
const authClient = createAuthClient(authClientOptions);
|
|
37
|
+
return {
|
|
38
|
+
slug: 'users',
|
|
39
|
+
access: {
|
|
40
|
+
admin: authenticated,
|
|
41
|
+
// Disable manual user management through Payload admin
|
|
42
|
+
// Users can only be managed through better-auth
|
|
43
|
+
create: ({ req })=>basicSigOk(req),
|
|
44
|
+
delete: ({ req })=>basicSigOk(req),
|
|
45
|
+
read: authenticated,
|
|
46
|
+
update: ({ req })=>basicSigOk(req)
|
|
47
|
+
},
|
|
48
|
+
admin: {
|
|
49
|
+
defaultColumns: [
|
|
50
|
+
'name',
|
|
51
|
+
'email'
|
|
52
|
+
],
|
|
53
|
+
useAsTitle: 'name'
|
|
54
|
+
},
|
|
55
|
+
auth: {
|
|
56
|
+
disableLocalStrategy: true,
|
|
57
|
+
strategies: [
|
|
58
|
+
{
|
|
59
|
+
name: 'better-auth',
|
|
60
|
+
authenticate: async ({ headers, payload })=>{
|
|
61
|
+
// Validate Better Auth session (cookie/JWT) from headers
|
|
62
|
+
const session = await authClient.getSession({
|
|
63
|
+
fetchOptions: {
|
|
64
|
+
headers
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (!session.data) {
|
|
68
|
+
return {
|
|
69
|
+
user: null
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const externalId = session.data.user.id;
|
|
73
|
+
// Find or provision the minimal Payload user
|
|
74
|
+
const existing = await payload.find({
|
|
75
|
+
collection: 'users',
|
|
76
|
+
limit: 1,
|
|
77
|
+
where: {
|
|
78
|
+
externalId: {
|
|
79
|
+
equals: externalId
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const doc = existing.docs[0] ?? await payload.create({
|
|
84
|
+
collection: 'users',
|
|
85
|
+
data: {
|
|
86
|
+
externalId
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
user: {
|
|
91
|
+
collection: 'users',
|
|
92
|
+
...doc
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
fields: [
|
|
100
|
+
{
|
|
101
|
+
name: 'externalId',
|
|
102
|
+
type: 'text',
|
|
103
|
+
index: true,
|
|
104
|
+
required: true,
|
|
105
|
+
unique: true
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'name',
|
|
109
|
+
type: 'text'
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
hooks: {
|
|
113
|
+
beforeChange: [
|
|
114
|
+
async ({ data, operation, originalDoc, req })=>{
|
|
115
|
+
if (operation === 'create') {
|
|
116
|
+
// authoritative check: tie signature to the actual mutation
|
|
117
|
+
const sig = req.context.baSig;
|
|
118
|
+
const expectedBody = {
|
|
119
|
+
op: 'create',
|
|
120
|
+
userId: data.externalId
|
|
121
|
+
};
|
|
122
|
+
if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {
|
|
123
|
+
return Promise.reject(new APIError('User creation is managed by Better Auth.'));
|
|
124
|
+
}
|
|
125
|
+
// mark nonce as used
|
|
126
|
+
seenNonces.set(sig.nonce, Date.now() + TTL);
|
|
127
|
+
} else if (operation === 'update') {
|
|
128
|
+
// authoritative check: tie signature to the actual mutation
|
|
129
|
+
const sig = req.context.baSig;
|
|
130
|
+
const userId = originalDoc?.externalId || data.externalId;
|
|
131
|
+
const expectedBody = {
|
|
132
|
+
op: 'update',
|
|
133
|
+
userId
|
|
134
|
+
};
|
|
135
|
+
if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {
|
|
136
|
+
return Promise.reject(new APIError('User updates are managed by Better Auth.'));
|
|
137
|
+
}
|
|
138
|
+
// mark nonce as used
|
|
139
|
+
seenNonces.set(sig.nonce, Date.now() + TTL);
|
|
140
|
+
}
|
|
141
|
+
return data;
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
beforeDelete: [
|
|
145
|
+
async ({ id, req })=>{
|
|
146
|
+
// Get the document first to access externalId
|
|
147
|
+
const doc = await req.payload.findByID({
|
|
148
|
+
id,
|
|
149
|
+
collection: 'users'
|
|
150
|
+
});
|
|
151
|
+
const sig = req.context.baSig;
|
|
152
|
+
const expectedBody = {
|
|
153
|
+
op: 'delete',
|
|
154
|
+
userId: doc.externalId
|
|
155
|
+
};
|
|
156
|
+
if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {
|
|
157
|
+
return Promise.reject(new APIError('User deletion is managed by Better Auth.'));
|
|
158
|
+
}
|
|
159
|
+
seenNonces.set(sig.nonce, Date.now() + TTL);
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
timestamps: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/Users/index.ts"],"sourcesContent":["import type { AccessArgs, CollectionConfig, PayloadRequest, User } from 'payload'\n\nimport { createAuthClient } from 'better-auth/react'\nimport { APIError } from 'payload'\n\nimport { type CryptoSignature, verifyCanonical } from '../../better-auth/crypto-shared.js'\n\nconst INTERNAL_SECRET = process.env.BA_TO_PAYLOAD_SECRET!\n\ntype isAuthenticated = (args: AccessArgs<User>) => boolean\nconst authenticated: isAuthenticated = ({ req: { user } }) => {\n return Boolean(user)\n}\n\n// (optional) simple anti-replay for Local API calls\nconst seenNonces = new Map<string, number>()\nconst TTL = 5 * 60 * 1000\nsetInterval(() => {\n const now = Date.now()\n for (const [k, exp] of seenNonces) {\n if (exp < now) {\n seenNonces.delete(k)\n }\n }\n}, 60_000).unref()\n\nfunction basicSigOk(req: { context: { baSig?: CryptoSignature } } & PayloadRequest) {\n const sig = req.context.baSig\n const body = req.context.baBody\n if (!sig || !body) {\n return false\n }\n const ok = verifyCanonical(body, sig, INTERNAL_SECRET)\n if (!ok) {\n return false\n }\n if (seenNonces.has(sig.nonce)) {\n return false\n } // replay\n // don't mark used yet; final verification happens in hooks\n return true\n}\n\nexport function createUsersCollection({\n authClientOptions,\n}: {\n authClientOptions: Parameters<typeof createAuthClient>['0']\n}): CollectionConfig {\n const authClient = createAuthClient(authClientOptions)\n return {\n slug: 'users',\n access: {\n admin: authenticated,\n // Disable manual user management through Payload admin\n // Users can only be managed through better-auth\n create: ({ req }) => basicSigOk(req),\n delete: ({ req }) => basicSigOk(req),\n read: authenticated,\n update: ({ req }) => basicSigOk(req),\n },\n admin: {\n defaultColumns: ['name', 'email'],\n useAsTitle: 'name',\n },\n auth: {\n disableLocalStrategy: true,\n strategies: [\n {\n name: 'better-auth',\n authenticate: async ({ headers, payload }) => {\n // Validate Better Auth session (cookie/JWT) from headers\n const session = await authClient.getSession({ fetchOptions: { headers } })\n if (!session.data) {\n return { user: null }\n }\n const externalId = session.data.user.id\n\n // Find or provision the minimal Payload user\n const existing = await payload.find({\n collection: 'users',\n limit: 1,\n where: { externalId: { equals: externalId } },\n })\n const doc =\n existing.docs[0] ??\n (await payload.create({\n collection: 'users',\n data: { externalId },\n }))\n\n return { user: { collection: 'users', ...doc } }\n },\n },\n ],\n },\n fields: [\n { name: 'externalId', type: 'text', index: true, required: true, unique: true },\n {\n name: 'name',\n type: 'text',\n },\n ],\n hooks: {\n beforeChange: [\n async ({ data, operation, originalDoc, req }) => {\n if (operation === 'create') {\n // authoritative check: tie signature to the actual mutation\n const sig = req.context.baSig as CryptoSignature | undefined\n const expectedBody = { op: 'create', userId: data.externalId }\n if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {\n return Promise.reject(new APIError('User creation is managed by Better Auth.'))\n }\n // mark nonce as used\n seenNonces.set(sig.nonce, Date.now() + TTL)\n } else if (operation === 'update') {\n // authoritative check: tie signature to the actual mutation\n const sig = req.context.baSig as CryptoSignature | undefined\n const userId = originalDoc?.externalId || data.externalId\n const expectedBody = { op: 'update', userId }\n if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {\n return Promise.reject(new APIError('User updates are managed by Better Auth.'))\n }\n // mark nonce as used\n seenNonces.set(sig.nonce, Date.now() + TTL)\n }\n return data\n },\n ],\n beforeDelete: [\n async ({ id, req }) => {\n // Get the document first to access externalId\n const doc = await req.payload.findByID({\n id,\n collection: 'users',\n })\n\n const sig = req.context.baSig as CryptoSignature | undefined\n const expectedBody = { op: 'delete', userId: doc.externalId }\n if (!sig || !verifyCanonical(expectedBody, sig, INTERNAL_SECRET)) {\n return Promise.reject(new APIError('User deletion is managed by Better Auth.'))\n }\n seenNonces.set(sig.nonce, Date.now() + TTL)\n },\n ],\n },\n timestamps: true,\n }\n}\n"],"names":["createAuthClient","APIError","verifyCanonical","INTERNAL_SECRET","process","env","BA_TO_PAYLOAD_SECRET","authenticated","req","user","Boolean","seenNonces","Map","TTL","setInterval","now","Date","k","exp","delete","unref","basicSigOk","sig","context","baSig","body","baBody","ok","has","nonce","createUsersCollection","authClientOptions","authClient","slug","access","admin","create","read","update","defaultColumns","useAsTitle","auth","disableLocalStrategy","strategies","name","authenticate","headers","payload","session","getSession","fetchOptions","data","externalId","id","existing","find","collection","limit","where","equals","doc","docs","fields","type","index","required","unique","hooks","beforeChange","operation","originalDoc","expectedBody","op","userId","Promise","reject","set","beforeDelete","findByID","timestamps"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useConfig } from '@payloadcms/ui';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
export const BeforeDashboardClient = ()=>{
|
|
6
|
+
const { config } = useConfig();
|
|
7
|
+
const [message, setMessage] = useState('');
|
|
8
|
+
useEffect(()=>{
|
|
9
|
+
const fetchMessage = async ()=>{
|
|
10
|
+
const response = await fetch(`${config.serverURL}${config.routes.api}/my-plugin-endpoint`);
|
|
11
|
+
const result = await response.json();
|
|
12
|
+
setMessage(result.message);
|
|
13
|
+
};
|
|
14
|
+
void fetchMessage();
|
|
15
|
+
}, [
|
|
16
|
+
config.serverURL,
|
|
17
|
+
config.routes.api
|
|
18
|
+
]);
|
|
19
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
20
|
+
children: [
|
|
21
|
+
/*#__PURE__*/ _jsx("h1", {
|
|
22
|
+
children: "Added by the plugin: Before Dashboard Client"
|
|
23
|
+
}),
|
|
24
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
25
|
+
children: [
|
|
26
|
+
"Message from the endpoint:",
|
|
27
|
+
/*#__PURE__*/ _jsx("div", {
|
|
28
|
+
children: message || 'Loading...'
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
})
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=BeforeDashboardClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/BeforeDashboardClient.tsx"],"sourcesContent":["'use client'\nimport { useConfig } from '@payloadcms/ui'\nimport { type default as React, useEffect, useState } from 'react'\n\nexport const BeforeDashboardClient = () => {\n const { config } = useConfig()\n\n const [message, setMessage] = useState('')\n\n useEffect(() => {\n const fetchMessage = async () => {\n const response = await fetch(`${config.serverURL}${config.routes.api}/my-plugin-endpoint`)\n const result = await response.json()\n setMessage(result.message)\n }\n\n void fetchMessage()\n }, [config.serverURL, config.routes.api])\n\n return (\n <div>\n <h1>Added by the plugin: Before Dashboard Client</h1>\n <div>\n Message from the endpoint:\n <div>{message || 'Loading...'}</div>\n </div>\n </div>\n )\n}\n"],"names":["useConfig","useEffect","useState","BeforeDashboardClient","config","message","setMessage","fetchMessage","response","fetch","serverURL","routes","api","result","json","div","h1"],"mappings":"AAAA;;AACA,SAASA,SAAS,QAAQ,iBAAgB;AAC1C,SAAgCC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAElE,OAAO,MAAMC,wBAAwB;IACnC,MAAM,EAAEC,MAAM,EAAE,GAAGJ;IAEnB,MAAM,CAACK,SAASC,WAAW,GAAGJ,SAAS;IAEvCD,UAAU;QACR,MAAMM,eAAe;YACnB,MAAMC,WAAW,MAAMC,MAAM,GAAGL,OAAOM,SAAS,GAAGN,OAAOO,MAAM,CAACC,GAAG,CAAC,mBAAmB,CAAC;YACzF,MAAMC,SAAS,MAAML,SAASM,IAAI;YAClCR,WAAWO,OAAOR,OAAO;QAC3B;QAEA,KAAKE;IACP,GAAG;QAACH,OAAOM,SAAS;QAAEN,OAAOO,MAAM,CAACC,GAAG;KAAC;IAExC,qBACE,MAACG;;0BACC,KAACC;0BAAG;;0BACJ,MAACD;;oBAAI;kCAEH,KAACA;kCAAKV,WAAW;;;;;;AAIzB,EAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styles from './BeforeDashboardServer.module.css';
|
|
3
|
+
export const BeforeDashboardServer = async (props)=>{
|
|
4
|
+
const { payload } = props;
|
|
5
|
+
const { docs } = await payload.find({
|
|
6
|
+
collection: 'plugin-collection'
|
|
7
|
+
});
|
|
8
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
9
|
+
className: styles.wrapper,
|
|
10
|
+
children: [
|
|
11
|
+
/*#__PURE__*/ _jsx("h1", {
|
|
12
|
+
children: "Added by the plugin: Before Dashboard Server"
|
|
13
|
+
}),
|
|
14
|
+
"Docs from Local API:",
|
|
15
|
+
docs.map((doc)=>/*#__PURE__*/ _jsx("div", {
|
|
16
|
+
children: doc.id
|
|
17
|
+
}, doc.id))
|
|
18
|
+
]
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=BeforeDashboardServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/BeforeDashboardServer.tsx"],"sourcesContent":["import type { ServerComponentProps } from 'payload'\nimport type React from 'react'\n\nimport styles from './BeforeDashboardServer.module.css'\n\nexport const BeforeDashboardServer = async (props: ServerComponentProps) => {\n const { payload } = props\n\n const { docs } = await payload.find({ collection: 'plugin-collection' })\n\n return (\n <div className={styles.wrapper}>\n <h1>Added by the plugin: Before Dashboard Server</h1>\n Docs from Local API:\n {docs.map((doc) => (\n <div key={doc.id}>{doc.id}</div>\n ))}\n </div>\n )\n}\n"],"names":["styles","BeforeDashboardServer","props","payload","docs","find","collection","div","className","wrapper","h1","map","doc","id"],"mappings":";AAGA,OAAOA,YAAY,qCAAoC;AAEvD,OAAO,MAAMC,wBAAwB,OAAOC;IAC1C,MAAM,EAAEC,OAAO,EAAE,GAAGD;IAEpB,MAAM,EAAEE,IAAI,EAAE,GAAG,MAAMD,QAAQE,IAAI,CAAC;QAAEC,YAAY;IAAoB;IAEtE,qBACE,MAACC;QAAIC,WAAWR,OAAOS,OAAO;;0BAC5B,KAACC;0BAAG;;YAAiD;YAEpDN,KAAKO,GAAG,CAAC,CAACC,oBACT,KAACL;8BAAkBK,IAAIC,EAAE;mBAAfD,IAAIC,EAAE;;;AAIxB,EAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EmailPasswordFormClient } from './EmailPasswordFormClient.js';
|
|
3
|
+
async function fetchAuthMethods() {
|
|
4
|
+
try {
|
|
5
|
+
const baseURL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000';
|
|
6
|
+
const response = await fetch(`${baseURL}/api/auth/auth/methods`, {
|
|
7
|
+
headers: {
|
|
8
|
+
'Content-Type': 'application/json'
|
|
9
|
+
},
|
|
10
|
+
method: 'GET'
|
|
11
|
+
});
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
throw new Error(`Failed to fetch auth methods: ${response.status}`);
|
|
14
|
+
}
|
|
15
|
+
const data = await response.json();
|
|
16
|
+
return data;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error('Error fetching auth methods:', error);
|
|
19
|
+
// Return default fallback
|
|
20
|
+
return {
|
|
21
|
+
authMethods: [
|
|
22
|
+
'emailAndPassword'
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function BetterAuthLoginServer({ authClientOptions }) {
|
|
28
|
+
const { authMethods } = await fetchAuthMethods();
|
|
29
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
30
|
+
style: {
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
display: 'flex',
|
|
33
|
+
justifyContent: 'center'
|
|
34
|
+
},
|
|
35
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
36
|
+
style: {
|
|
37
|
+
background: 'white',
|
|
38
|
+
borderRadius: '8px',
|
|
39
|
+
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
40
|
+
maxWidth: '400px',
|
|
41
|
+
padding: '2rem',
|
|
42
|
+
width: '100%'
|
|
43
|
+
},
|
|
44
|
+
children: [
|
|
45
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
46
|
+
style: {
|
|
47
|
+
color: '#333',
|
|
48
|
+
fontSize: '1.5rem',
|
|
49
|
+
fontWeight: '600',
|
|
50
|
+
marginBottom: '2rem',
|
|
51
|
+
textAlign: 'center'
|
|
52
|
+
},
|
|
53
|
+
children: "Sign In to Admin"
|
|
54
|
+
}),
|
|
55
|
+
authMethods.includes('emailAndPassword') && /*#__PURE__*/ _jsx(EmailPasswordFormClient, {
|
|
56
|
+
authClientOptions: authClientOptions
|
|
57
|
+
}),
|
|
58
|
+
authMethods.length === 0 && /*#__PURE__*/ _jsxs("div", {
|
|
59
|
+
style: {
|
|
60
|
+
color: '#666',
|
|
61
|
+
padding: '2rem',
|
|
62
|
+
textAlign: 'center'
|
|
63
|
+
},
|
|
64
|
+
children: [
|
|
65
|
+
/*#__PURE__*/ _jsx("p", {
|
|
66
|
+
children: "No authentication methods are currently available."
|
|
67
|
+
}),
|
|
68
|
+
/*#__PURE__*/ _jsx("p", {
|
|
69
|
+
style: {
|
|
70
|
+
fontSize: '0.875rem',
|
|
71
|
+
marginTop: '1rem'
|
|
72
|
+
},
|
|
73
|
+
children: "Please contact your administrator."
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
})
|
|
77
|
+
]
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=BetterAuthLoginServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/BetterAuthLoginServer.tsx"],"sourcesContent":["import type { createAuthClient } from 'better-auth/react'\nimport type React from 'react'\n\nimport { EmailPasswordFormClient } from './EmailPasswordFormClient.js'\n\ninterface AuthMethods {\n authMethods: 'emailAndPassword'[]\n}\n\nasync function fetchAuthMethods(): Promise<AuthMethods> {\n try {\n const baseURL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000'\n const response = await fetch(`${baseURL}/api/auth/auth/methods`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'GET',\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch auth methods: ${response.status}`)\n }\n\n const data = await response.json()\n return data as AuthMethods\n } catch (error) {\n console.error('Error fetching auth methods:', error)\n // Return default fallback\n return { authMethods: ['emailAndPassword'] }\n }\n}\n\nexport async function BetterAuthLoginServer({\n authClientOptions,\n}: {\n authClientOptions: Parameters<typeof createAuthClient>['0']\n}) {\n const { authMethods } = await fetchAuthMethods()\n\n return (\n <div\n style={{\n alignItems: 'center',\n display: 'flex',\n justifyContent: 'center',\n }}\n >\n <div\n style={{\n background: 'white',\n borderRadius: '8px',\n boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',\n maxWidth: '400px',\n padding: '2rem',\n width: '100%',\n }}\n >\n <h2\n style={{\n color: '#333',\n fontSize: '1.5rem',\n fontWeight: '600',\n marginBottom: '2rem',\n textAlign: 'center',\n }}\n >\n Sign In to Admin\n </h2>\n\n {authMethods.includes('emailAndPassword') && (\n <EmailPasswordFormClient authClientOptions={authClientOptions} />\n )}\n {authMethods.length === 0 && (\n <div\n style={{\n color: '#666',\n padding: '2rem',\n textAlign: 'center',\n }}\n >\n <p>No authentication methods are currently available.</p>\n <p style={{ fontSize: '0.875rem', marginTop: '1rem' }}>\n Please contact your administrator.\n </p>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":["EmailPasswordFormClient","fetchAuthMethods","baseURL","process","env","NEXT_PUBLIC_SERVER_URL","response","fetch","headers","method","ok","Error","status","data","json","error","console","authMethods","BetterAuthLoginServer","authClientOptions","div","style","alignItems","display","justifyContent","background","borderRadius","boxShadow","maxWidth","padding","width","h2","color","fontSize","fontWeight","marginBottom","textAlign","includes","length","p","marginTop"],"mappings":";AAGA,SAASA,uBAAuB,QAAQ,+BAA8B;AAMtE,eAAeC;IACb,IAAI;QACF,MAAMC,UAAUC,QAAQC,GAAG,CAACC,sBAAsB,IAAI;QACtD,MAAMC,WAAW,MAAMC,MAAM,GAAGL,QAAQ,sBAAsB,CAAC,EAAE;YAC/DM,SAAS;gBACP,gBAAgB;YAClB;YACAC,QAAQ;QACV;QAEA,IAAI,CAACH,SAASI,EAAE,EAAE;YAChB,MAAM,IAAIC,MAAM,CAAC,8BAA8B,EAAEL,SAASM,MAAM,EAAE;QACpE;QAEA,MAAMC,OAAO,MAAMP,SAASQ,IAAI;QAChC,OAAOD;IACT,EAAE,OAAOE,OAAO;QACdC,QAAQD,KAAK,CAAC,gCAAgCA;QAC9C,0BAA0B;QAC1B,OAAO;YAAEE,aAAa;gBAAC;aAAmB;QAAC;IAC7C;AACF;AAEA,OAAO,eAAeC,sBAAsB,EAC1CC,iBAAiB,EAGlB;IACC,MAAM,EAAEF,WAAW,EAAE,GAAG,MAAMhB;IAE9B,qBACE,KAACmB;QACCC,OAAO;YACLC,YAAY;YACZC,SAAS;YACTC,gBAAgB;QAClB;kBAEA,cAAA,MAACJ;YACCC,OAAO;gBACLI,YAAY;gBACZC,cAAc;gBACdC,WAAW;gBACXC,UAAU;gBACVC,SAAS;gBACTC,OAAO;YACT;;8BAEA,KAACC;oBACCV,OAAO;wBACLW,OAAO;wBACPC,UAAU;wBACVC,YAAY;wBACZC,cAAc;wBACdC,WAAW;oBACb;8BACD;;gBAIAnB,YAAYoB,QAAQ,CAAC,qCACpB,KAACrC;oBAAwBmB,mBAAmBA;;gBAE7CF,YAAYqB,MAAM,KAAK,mBACtB,MAAClB;oBACCC,OAAO;wBACLW,OAAO;wBACPH,SAAS;wBACTO,WAAW;oBACb;;sCAEA,KAACG;sCAAE;;sCACH,KAACA;4BAAElB,OAAO;gCAAEY,UAAU;gCAAYO,WAAW;4BAAO;sCAAG;;;;;;;AAQnE"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, FieldLabel, TextInput } from '@payloadcms/ui';
|
|
4
|
+
import { createAuthClient } from 'better-auth/react';
|
|
5
|
+
import { useRouter } from 'next/navigation.js';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
export function EmailPasswordFormClient({ authClientOptions }) {
|
|
8
|
+
const authClient = createAuthClient(authClientOptions);
|
|
9
|
+
const router = useRouter();
|
|
10
|
+
const [errors, setErrors] = useState({});
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
// Use useField hooks for each input to get proper setValue functions
|
|
13
|
+
const [emailValue, setEmailValue] = useState('');
|
|
14
|
+
const [passwordValue, setPasswordValue] = useState('');
|
|
15
|
+
const handleEmailChange = (event)=>{
|
|
16
|
+
setEmailValue(event.target.value);
|
|
17
|
+
// Clear field-specific error when user starts typing
|
|
18
|
+
if (errors.email) {
|
|
19
|
+
setErrors((prev)=>({
|
|
20
|
+
...prev,
|
|
21
|
+
email: undefined
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const handlePasswordChange = (event)=>{
|
|
26
|
+
setPasswordValue(event.target.value);
|
|
27
|
+
// Clear field-specific error when user starts typing
|
|
28
|
+
if (errors.password) {
|
|
29
|
+
setErrors((prev)=>({
|
|
30
|
+
...prev,
|
|
31
|
+
password: undefined
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const validateForm = ()=>{
|
|
36
|
+
const newErrors = {};
|
|
37
|
+
const email = String(emailValue || '').trim();
|
|
38
|
+
const password = String(passwordValue || '').trim();
|
|
39
|
+
if (!email) {
|
|
40
|
+
newErrors.email = 'Email is required';
|
|
41
|
+
} else if (!/^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/.test(email)) {
|
|
42
|
+
newErrors.email = 'Please enter a valid email address';
|
|
43
|
+
}
|
|
44
|
+
if (!password) {
|
|
45
|
+
newErrors.password = 'Password is required';
|
|
46
|
+
} else if (password.length < 6) {
|
|
47
|
+
newErrors.password = 'Password must be at least 6 characters';
|
|
48
|
+
}
|
|
49
|
+
setErrors(newErrors);
|
|
50
|
+
return Object.keys(newErrors).length === 0;
|
|
51
|
+
};
|
|
52
|
+
const handleSubmit = async (e)=>{
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
if (!validateForm()) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setIsLoading(true);
|
|
58
|
+
setErrors({});
|
|
59
|
+
try {
|
|
60
|
+
const result = await authClient.signIn.email({
|
|
61
|
+
email: String(emailValue || ''),
|
|
62
|
+
password: String(passwordValue || '')
|
|
63
|
+
});
|
|
64
|
+
if (result.error) {
|
|
65
|
+
setErrors({
|
|
66
|
+
general: result.error.message || 'Sign in failed. Please check your credentials.'
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
// Successful sign in - redirect to admin
|
|
70
|
+
router.push('/admin');
|
|
71
|
+
router.refresh();
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('Sign in error:', error);
|
|
75
|
+
setErrors({
|
|
76
|
+
general: 'An unexpected error occurred. Please try again.'
|
|
77
|
+
});
|
|
78
|
+
} finally{
|
|
79
|
+
setIsLoading(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const errorStyle = {
|
|
83
|
+
color: '#dc2626',
|
|
84
|
+
fontSize: '0.875rem',
|
|
85
|
+
marginTop: '0.25rem'
|
|
86
|
+
};
|
|
87
|
+
return /*#__PURE__*/ _jsxs("form", {
|
|
88
|
+
className: "email-password-form",
|
|
89
|
+
onSubmit: handleSubmit,
|
|
90
|
+
children: [
|
|
91
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
92
|
+
className: "form-field",
|
|
93
|
+
style: {
|
|
94
|
+
marginBottom: '1.5rem'
|
|
95
|
+
},
|
|
96
|
+
children: [
|
|
97
|
+
/*#__PURE__*/ _jsx(FieldLabel, {
|
|
98
|
+
htmlFor: "email",
|
|
99
|
+
label: "Email",
|
|
100
|
+
required: true
|
|
101
|
+
}),
|
|
102
|
+
/*#__PURE__*/ _jsx(TextInput, {
|
|
103
|
+
onChange: handleEmailChange,
|
|
104
|
+
path: "email",
|
|
105
|
+
readOnly: isLoading,
|
|
106
|
+
value: emailValue || ''
|
|
107
|
+
}),
|
|
108
|
+
errors.email && /*#__PURE__*/ _jsx("div", {
|
|
109
|
+
className: "field-error",
|
|
110
|
+
style: errorStyle,
|
|
111
|
+
children: errors.email
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
}),
|
|
115
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
116
|
+
className: "form-field",
|
|
117
|
+
style: {
|
|
118
|
+
marginBottom: '1.5rem'
|
|
119
|
+
},
|
|
120
|
+
children: [
|
|
121
|
+
/*#__PURE__*/ _jsx(FieldLabel, {
|
|
122
|
+
htmlFor: "password",
|
|
123
|
+
label: "Password",
|
|
124
|
+
required: true
|
|
125
|
+
}),
|
|
126
|
+
/*#__PURE__*/ _jsx(TextInput, {
|
|
127
|
+
onChange: handlePasswordChange,
|
|
128
|
+
path: "password",
|
|
129
|
+
readOnly: isLoading,
|
|
130
|
+
value: passwordValue || ''
|
|
131
|
+
}),
|
|
132
|
+
errors.password && /*#__PURE__*/ _jsx("div", {
|
|
133
|
+
className: "field-error",
|
|
134
|
+
style: errorStyle,
|
|
135
|
+
children: errors.password
|
|
136
|
+
})
|
|
137
|
+
]
|
|
138
|
+
}),
|
|
139
|
+
errors.general && /*#__PURE__*/ _jsx("div", {
|
|
140
|
+
className: "general-error",
|
|
141
|
+
style: {
|
|
142
|
+
...errorStyle,
|
|
143
|
+
backgroundColor: '#fef2f2',
|
|
144
|
+
border: '1px solid #fecaca',
|
|
145
|
+
borderRadius: '0.375rem',
|
|
146
|
+
marginBottom: '1rem',
|
|
147
|
+
padding: '0.75rem'
|
|
148
|
+
},
|
|
149
|
+
children: errors.general
|
|
150
|
+
}),
|
|
151
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
152
|
+
buttonStyle: "primary",
|
|
153
|
+
disabled: isLoading,
|
|
154
|
+
size: "large",
|
|
155
|
+
type: "submit",
|
|
156
|
+
children: isLoading ? 'Signing In...' : 'Sign In'
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=EmailPasswordFormClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/EmailPasswordFormClient.tsx"],"sourcesContent":["'use client'\n\nimport type { ChangeEvent, default as React } from 'react'\n\nimport { Button, FieldLabel, TextInput } from '@payloadcms/ui'\nimport { createAuthClient } from 'better-auth/react'\nimport { useRouter } from 'next/navigation.js'\nimport { useState } from 'react'\n\ninterface FormErrors {\n email?: string\n general?: string\n password?: string\n}\n\nexport function EmailPasswordFormClient({\n authClientOptions,\n}: {\n authClientOptions: Parameters<typeof createAuthClient>['0']\n}) {\n const authClient = createAuthClient(authClientOptions)\n const router = useRouter()\n const [errors, setErrors] = useState<FormErrors>({})\n const [isLoading, setIsLoading] = useState(false)\n\n // Use useField hooks for each input to get proper setValue functions\n const [emailValue, setEmailValue] = useState('')\n const [passwordValue, setPasswordValue] = useState('')\n\n const handleEmailChange = (event: ChangeEvent<HTMLInputElement>) => {\n setEmailValue(event.target.value)\n // Clear field-specific error when user starts typing\n if (errors.email) {\n setErrors((prev) => ({ ...prev, email: undefined }))\n }\n }\n\n const handlePasswordChange = (event: ChangeEvent<HTMLInputElement>) => {\n setPasswordValue(event.target.value)\n // Clear field-specific error when user starts typing\n if (errors.password) {\n setErrors((prev) => ({ ...prev, password: undefined }))\n }\n }\n\n const validateForm = (): boolean => {\n const newErrors: FormErrors = {}\n const email = String(emailValue || '').trim()\n const password = String(passwordValue || '').trim()\n\n if (!email) {\n newErrors.email = 'Email is required'\n } else if (!/^[^\\s@]+@[^\\s@][^\\s.@]*\\.[^\\s@]+$/.test(email)) {\n newErrors.email = 'Please enter a valid email address'\n }\n\n if (!password) {\n newErrors.password = 'Password is required'\n } else if (password.length < 6) {\n newErrors.password = 'Password must be at least 6 characters'\n }\n\n setErrors(newErrors)\n return Object.keys(newErrors).length === 0\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n\n if (!validateForm()) {\n return\n }\n\n setIsLoading(true)\n setErrors({})\n\n try {\n const result = await authClient.signIn.email({\n email: String(emailValue || ''),\n password: String(passwordValue || ''),\n })\n\n if (result.error) {\n setErrors({\n general: result.error.message || 'Sign in failed. Please check your credentials.',\n })\n } else {\n // Successful sign in - redirect to admin\n router.push('/admin')\n router.refresh()\n }\n } catch (error) {\n console.error('Sign in error:', error)\n setErrors({\n general: 'An unexpected error occurred. Please try again.',\n })\n } finally {\n setIsLoading(false)\n }\n }\n\n const errorStyle = {\n color: '#dc2626',\n fontSize: '0.875rem',\n marginTop: '0.25rem',\n }\n\n return (\n <form className=\"email-password-form\" onSubmit={handleSubmit}>\n <div className=\"form-field\" style={{ marginBottom: '1.5rem' }}>\n <FieldLabel htmlFor=\"email\" label=\"Email\" required />\n <TextInput\n onChange={handleEmailChange}\n path=\"email\"\n readOnly={isLoading}\n value={emailValue || ''}\n />\n {errors.email && (\n <div className=\"field-error\" style={errorStyle}>\n {errors.email}\n </div>\n )}\n </div>\n\n <div className=\"form-field\" style={{ marginBottom: '1.5rem' }}>\n <FieldLabel htmlFor=\"password\" label=\"Password\" required />\n <TextInput\n onChange={handlePasswordChange}\n path=\"password\"\n readOnly={isLoading}\n value={passwordValue || ''}\n />\n {errors.password && (\n <div className=\"field-error\" style={errorStyle}>\n {errors.password}\n </div>\n )}\n </div>\n\n {errors.general && (\n <div\n className=\"general-error\"\n style={{\n ...errorStyle,\n backgroundColor: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: '0.375rem',\n marginBottom: '1rem',\n padding: '0.75rem',\n }}\n >\n {errors.general}\n </div>\n )}\n\n <Button buttonStyle=\"primary\" disabled={isLoading} size=\"large\" type=\"submit\">\n {isLoading ? 'Signing In...' : 'Sign In'}\n </Button>\n </form>\n )\n}\n"],"names":["Button","FieldLabel","TextInput","createAuthClient","useRouter","useState","EmailPasswordFormClient","authClientOptions","authClient","router","errors","setErrors","isLoading","setIsLoading","emailValue","setEmailValue","passwordValue","setPasswordValue","handleEmailChange","event","target","value","email","prev","undefined","handlePasswordChange","password","validateForm","newErrors","String","trim","test","length","Object","keys","handleSubmit","e","preventDefault","result","signIn","error","general","message","push","refresh","console","errorStyle","color","fontSize","marginTop","form","className","onSubmit","div","style","marginBottom","htmlFor","label","required","onChange","path","readOnly","backgroundColor","border","borderRadius","padding","buttonStyle","disabled","size","type"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BetterAuthLoginServer } from '../components/BetterAuthLoginServer.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BetterAuthLoginServer } from '../components/BetterAuthLoginServer.js'\n"],"names":["BetterAuthLoginServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createDatabaseHooks } from './better-auth/databaseHooks.js';
|
|
2
|
+
export { payloadBetterAuthPlugin } from './better-auth/plugin.js';
|
|
3
|
+
export { Queue } from './better-auth/reconcile-queue.js';
|
|
4
|
+
export { betterAuthPayloadPlugin } from './payload/plugin.js';
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createDatabaseHooks } from './better-auth/databaseHooks.js'\nexport { payloadBetterAuthPlugin } from './better-auth/plugin.js'\nexport { Queue } from './better-auth/reconcile-queue.js'\nexport { betterAuthPayloadPlugin } from './payload/plugin.js'\n"],"names":["createDatabaseHooks","payloadBetterAuthPlugin","Queue","betterAuthPayloadPlugin"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,iCAAgC;AACpE,SAASC,uBAAuB,QAAQ,0BAAyB;AACjE,SAASC,KAAK,QAAQ,mCAAkC;AACxD,SAASC,uBAAuB,QAAQ,sBAAqB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { createAuthClient } from 'better-auth/react';
|
|
2
|
+
import type { Config } from 'payload';
|
|
3
|
+
export type BetterAuthPayloadPluginOptions = {
|
|
4
|
+
authClientOptions: Parameters<typeof createAuthClient>['0'];
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const betterAuthPayloadPlugin: (pluginOptions: BetterAuthPayloadPluginOptions) => (config: Config) => Config;
|