@main12/auth-login 0.3.0 → 0.3.1
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 +198 -40
- package/dist/auth/application/hooks/useLoginFlow.js +18 -2
- package/dist/auth/application/hooks/useVerifyOtpFlow.js +8 -2
- package/dist/auth/application/services/authService.d.ts +1 -0
- package/dist/auth/application/services/authService.js +2 -1
- package/dist/components/AuthClientInit.d.ts +27 -0
- package/dist/components/AuthClientInit.js +29 -0
- package/dist/components/AuthPages.d.ts +7 -1
- package/dist/components/AuthPages.js +18 -2
- package/dist/components/AuthPagesServer.d.ts +17 -0
- package/dist/components/AuthPagesServer.js +26 -0
- package/dist/components/pages/ForgotPasswordPageHero.js +1 -1
- package/dist/components/pages/LoginPageHero.js +2 -2
- package/dist/components/pages/LoginPageTailwind.js +25 -6
- package/dist/components/pages/SetPasswordPageHero.js +4 -2
- package/dist/components/pages/SignupPageTailwind.js +23 -4
- package/dist/components/pages/VerifyOtpPageHero.js +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +5 -1
- package/dist/endpoints/authEndpoints.js +113 -95
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.js +1 -0
- package/dist/exports/rsc.d.ts +2 -0
- package/dist/exports/rsc.js +2 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.js +104 -19
- package/dist/proxy.js +3 -2
- package/package.json +8 -2
- package/dist/endpoints/googleOAuth.d.ts +0 -10
- package/dist/endpoints/googleOAuth.js +0 -144
|
@@ -21,7 +21,7 @@ function LoginContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth = tru
|
|
|
21
21
|
poweredBy: poweredBy,
|
|
22
22
|
cardClassName: cardClassName,
|
|
23
23
|
backgroundClass: backgroundClass,
|
|
24
|
-
footer: /*#__PURE__*/ _jsxs("p", {
|
|
24
|
+
footer: signupUrl ? /*#__PURE__*/ _jsxs("p", {
|
|
25
25
|
className: "text-center text-gray-600 text-sm",
|
|
26
26
|
children: [
|
|
27
27
|
"Don't have an account?",
|
|
@@ -32,7 +32,7 @@ function LoginContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth = tru
|
|
|
32
32
|
children: "Sign up"
|
|
33
33
|
})
|
|
34
34
|
]
|
|
35
|
-
}),
|
|
35
|
+
}) : undefined,
|
|
36
36
|
children: [
|
|
37
37
|
step === 'email' && /*#__PURE__*/ _jsxs("div", {
|
|
38
38
|
className: "animate-[fadeIn_0.3s_ease-out]",
|
|
@@ -46,11 +46,30 @@ function LoginContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth = tru
|
|
|
46
46
|
className: "mb-4",
|
|
47
47
|
onPress: handleGoogleLogin,
|
|
48
48
|
children: [
|
|
49
|
-
/*#__PURE__*/
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
/*#__PURE__*/ _jsxs("svg", {
|
|
50
|
+
width: "20",
|
|
51
|
+
height: "20",
|
|
52
|
+
viewBox: "0 0 48 48",
|
|
53
|
+
children: [
|
|
54
|
+
/*#__PURE__*/ _jsx("path", {
|
|
55
|
+
fill: "#EA4335",
|
|
56
|
+
d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsx("path", {
|
|
59
|
+
fill: "#4285F4",
|
|
60
|
+
d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
|
|
61
|
+
}),
|
|
62
|
+
/*#__PURE__*/ _jsx("path", {
|
|
63
|
+
fill: "#FBBC05",
|
|
64
|
+
d: "M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.01 24.01 0 0 0 0 21.56l7.98-6.19z"
|
|
65
|
+
}),
|
|
66
|
+
/*#__PURE__*/ _jsx("path", {
|
|
67
|
+
fill: "#34A853",
|
|
68
|
+
d: "M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
|
69
|
+
})
|
|
70
|
+
]
|
|
52
71
|
}),
|
|
53
|
-
"
|
|
72
|
+
"Continue with Google"
|
|
54
73
|
]
|
|
55
74
|
}),
|
|
56
75
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -50,7 +50,8 @@ export default function SetPasswordPageHero({ redirectTo = '/', logo, poweredBy,
|
|
|
50
50
|
className: "relative",
|
|
51
51
|
children: [
|
|
52
52
|
/*#__PURE__*/ _jsx(Input, {
|
|
53
|
-
|
|
53
|
+
variant: "secondary",
|
|
54
|
+
className: "w-full pr-10"
|
|
54
55
|
}),
|
|
55
56
|
/*#__PURE__*/ _jsx("button", {
|
|
56
57
|
type: "button",
|
|
@@ -84,7 +85,8 @@ export default function SetPasswordPageHero({ redirectTo = '/', logo, poweredBy,
|
|
|
84
85
|
children: "Confirm Password"
|
|
85
86
|
}),
|
|
86
87
|
/*#__PURE__*/ _jsx(Input, {
|
|
87
|
-
|
|
88
|
+
variant: "secondary",
|
|
89
|
+
className: "w-full"
|
|
88
90
|
})
|
|
89
91
|
]
|
|
90
92
|
}),
|
|
@@ -52,11 +52,30 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
52
52
|
size: "lg",
|
|
53
53
|
className: "mb-4",
|
|
54
54
|
children: [
|
|
55
|
-
/*#__PURE__*/
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
/*#__PURE__*/ _jsxs("svg", {
|
|
56
|
+
width: "20",
|
|
57
|
+
height: "20",
|
|
58
|
+
viewBox: "0 0 48 48",
|
|
59
|
+
children: [
|
|
60
|
+
/*#__PURE__*/ _jsx("path", {
|
|
61
|
+
fill: "#EA4335",
|
|
62
|
+
d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ _jsx("path", {
|
|
65
|
+
fill: "#4285F4",
|
|
66
|
+
d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
|
|
67
|
+
}),
|
|
68
|
+
/*#__PURE__*/ _jsx("path", {
|
|
69
|
+
fill: "#FBBC05",
|
|
70
|
+
d: "M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.01 24.01 0 0 0 0 21.56l7.98-6.19z"
|
|
71
|
+
}),
|
|
72
|
+
/*#__PURE__*/ _jsx("path", {
|
|
73
|
+
fill: "#34A853",
|
|
74
|
+
d: "M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
|
75
|
+
})
|
|
76
|
+
]
|
|
58
77
|
}),
|
|
59
|
-
"
|
|
78
|
+
"Continue with Google"
|
|
60
79
|
]
|
|
61
80
|
}),
|
|
62
81
|
/*#__PURE__*/ _jsxs("div", {
|
package/dist/config.d.ts
CHANGED
|
@@ -11,9 +11,13 @@ export declare const pluginConfig: {
|
|
|
11
11
|
googleOAuthEnabled: boolean;
|
|
12
12
|
routeRedirects: boolean;
|
|
13
13
|
authBasePath: string;
|
|
14
|
+
passwordLogin: boolean;
|
|
15
|
+
otpLogin: boolean;
|
|
14
16
|
};
|
|
15
17
|
/** Call this client-side to mirror the server plugin config. */
|
|
16
18
|
export declare function initClientConfig(opts: {
|
|
17
19
|
style?: AuthStyle;
|
|
18
20
|
googleOAuthEnabled?: boolean;
|
|
21
|
+
passwordLogin?: boolean;
|
|
22
|
+
otpLogin?: boolean;
|
|
19
23
|
}): void;
|
package/dist/config.js
CHANGED
|
@@ -2,9 +2,13 @@ export const pluginConfig = {
|
|
|
2
2
|
style: 'tailwind',
|
|
3
3
|
googleOAuthEnabled: false,
|
|
4
4
|
routeRedirects: false,
|
|
5
|
-
authBasePath: '/auth'
|
|
5
|
+
authBasePath: '/auth',
|
|
6
|
+
passwordLogin: true,
|
|
7
|
+
otpLogin: true
|
|
6
8
|
};
|
|
7
9
|
/** Call this client-side to mirror the server plugin config. */ export function initClientConfig(opts) {
|
|
8
10
|
if (opts.style) pluginConfig.style = opts.style;
|
|
9
11
|
if (opts.googleOAuthEnabled !== undefined) pluginConfig.googleOAuthEnabled = opts.googleOAuthEnabled;
|
|
12
|
+
if (opts.passwordLogin !== undefined) pluginConfig.passwordLogin = opts.passwordLogin;
|
|
13
|
+
if (opts.otpLogin !== undefined) pluginConfig.otpLogin = opts.otpLogin;
|
|
10
14
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
1
2
|
import { generateOtp, hashOtp, verifyOtp, getOtpExpiry, isOtpExpired } from '../auth/domain/otp.js';
|
|
2
3
|
import { generateWelcomeEmail, generateOtpEmail } from '../components/email/index.js';
|
|
3
4
|
/**
|
|
4
5
|
* POST /api/auth/check-email — Check if user exists and has password
|
|
5
6
|
*/ export const checkEmailEndpoint = {
|
|
6
|
-
path: '/
|
|
7
|
+
path: '/auth/check-email',
|
|
7
8
|
method: 'post',
|
|
8
9
|
handler: async (req)=>{
|
|
9
10
|
const { email } = req.json ? await req.json() : req.body || {};
|
|
@@ -47,7 +48,7 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
47
48
|
/**
|
|
48
49
|
* POST /api/auth/otp/send — Generate OTP, store it, send email via Payload
|
|
49
50
|
*/ export const sendOtpEndpoint = {
|
|
50
|
-
path: '/
|
|
51
|
+
path: '/auth/otp/send',
|
|
51
52
|
method: 'post',
|
|
52
53
|
handler: async (req)=>{
|
|
53
54
|
const body = req.json ? await req.json() : req.body || {};
|
|
@@ -81,33 +82,32 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
81
82
|
const otp = generateOtp();
|
|
82
83
|
const hashed = hashOtp(otp);
|
|
83
84
|
const expiresAt = getOtpExpiry(10);
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
id: user.id,
|
|
91
|
-
data: {
|
|
92
|
-
otpHash: hashed,
|
|
93
|
-
otpPurpose: purpose,
|
|
94
|
-
otpAttempts: 0,
|
|
95
|
-
otpExpiresAt: expiresAt.toISOString()
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
} catch {
|
|
99
|
-
// If the users collection doesn't have OTP fields, create a dedicated otps doc
|
|
100
|
-
await req.payload.create({
|
|
101
|
-
collection: 'otps',
|
|
102
|
-
data: {
|
|
103
|
-
email: email.toLowerCase().trim(),
|
|
104
|
-
hash: hashed,
|
|
105
|
-
purpose,
|
|
106
|
-
attempts: 0,
|
|
107
|
-
expiresAt: expiresAt.toISOString()
|
|
85
|
+
// Delete any existing OTPs for this email
|
|
86
|
+
const existingOtps = await req.payload.find({
|
|
87
|
+
collection: 'auth-otps',
|
|
88
|
+
where: {
|
|
89
|
+
email: {
|
|
90
|
+
equals: email.toLowerCase().trim()
|
|
108
91
|
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
for (const doc of existingOtps.docs){
|
|
95
|
+
await req.payload.delete({
|
|
96
|
+
collection: 'auth-otps',
|
|
97
|
+
id: doc.id
|
|
109
98
|
});
|
|
110
99
|
}
|
|
100
|
+
// Store OTP in dedicated collection
|
|
101
|
+
await req.payload.create({
|
|
102
|
+
collection: 'auth-otps',
|
|
103
|
+
data: {
|
|
104
|
+
email: email.toLowerCase().trim(),
|
|
105
|
+
hash: hashed,
|
|
106
|
+
purpose,
|
|
107
|
+
attempts: 0,
|
|
108
|
+
expiresAt: expiresAt.toISOString()
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
111
|
// Send email via Payload's configured adapter
|
|
112
112
|
const emailResult = purpose === 'signup' ? generateOtpEmail({
|
|
113
113
|
userName: user.name || user.email,
|
|
@@ -140,7 +140,7 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
140
140
|
/**
|
|
141
141
|
* POST /api/auth/otp/verify — Verify OTP and login the user
|
|
142
142
|
*/ export const verifyOtpEndpoint = {
|
|
143
|
-
path: '/
|
|
143
|
+
path: '/auth/otp/verify',
|
|
144
144
|
method: 'post',
|
|
145
145
|
handler: async (req)=>{
|
|
146
146
|
const body = req.json ? await req.json() : req.body || {};
|
|
@@ -152,44 +152,19 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
152
152
|
status: 400
|
|
153
153
|
});
|
|
154
154
|
try {
|
|
155
|
-
// Find OTP record
|
|
156
|
-
const
|
|
157
|
-
collection: '
|
|
155
|
+
// Find OTP record from dedicated collection
|
|
156
|
+
const otpDocs = await req.payload.find({
|
|
157
|
+
collection: 'auth-otps',
|
|
158
158
|
where: {
|
|
159
159
|
email: {
|
|
160
160
|
equals: email.toLowerCase().trim()
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
|
+
sort: '-createdAt',
|
|
163
164
|
limit: 1
|
|
164
165
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
let otpExpiresAt;
|
|
168
|
-
const user = users.docs?.[0];
|
|
169
|
-
if (user?.otpHash) {
|
|
170
|
-
otpHash = user.otpHash;
|
|
171
|
-
otpAttempts = user.otpAttempts || 0;
|
|
172
|
-
otpExpiresAt = user.otpExpiresAt;
|
|
173
|
-
} else {
|
|
174
|
-
// Try dedicated otps collection
|
|
175
|
-
const otpDocs = await req.payload.find({
|
|
176
|
-
collection: 'otps',
|
|
177
|
-
where: {
|
|
178
|
-
email: {
|
|
179
|
-
equals: email.toLowerCase().trim()
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
sort: '-createdAt',
|
|
183
|
-
limit: 1
|
|
184
|
-
});
|
|
185
|
-
const otpDoc = otpDocs.docs?.[0];
|
|
186
|
-
if (otpDoc) {
|
|
187
|
-
otpHash = otpDoc.hash;
|
|
188
|
-
otpAttempts = otpDoc.attempts || 0;
|
|
189
|
-
otpExpiresAt = otpDoc.expiresAt;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (!otpHash) {
|
|
166
|
+
const otpDoc = otpDocs.docs?.[0];
|
|
167
|
+
if (!otpDoc?.hash) {
|
|
193
168
|
return Response.json({
|
|
194
169
|
success: false,
|
|
195
170
|
error: 'No OTP found. Please request a new code.'
|
|
@@ -197,7 +172,11 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
197
172
|
status: 400
|
|
198
173
|
});
|
|
199
174
|
}
|
|
200
|
-
if (isOtpExpired(
|
|
175
|
+
if (isOtpExpired(otpDoc.expiresAt)) {
|
|
176
|
+
await req.payload.delete({
|
|
177
|
+
collection: 'auth-otps',
|
|
178
|
+
id: otpDoc.id
|
|
179
|
+
});
|
|
201
180
|
return Response.json({
|
|
202
181
|
success: false,
|
|
203
182
|
error: 'Code has expired. Please request a new one.'
|
|
@@ -205,7 +184,11 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
205
184
|
status: 400
|
|
206
185
|
});
|
|
207
186
|
}
|
|
208
|
-
if (
|
|
187
|
+
if ((otpDoc.attempts || 0) >= 3) {
|
|
188
|
+
await req.payload.delete({
|
|
189
|
+
collection: 'auth-otps',
|
|
190
|
+
id: otpDoc.id
|
|
191
|
+
});
|
|
209
192
|
return Response.json({
|
|
210
193
|
success: false,
|
|
211
194
|
error: 'Too many attempts. Please request a new code.'
|
|
@@ -214,16 +197,14 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
214
197
|
});
|
|
215
198
|
}
|
|
216
199
|
// Increment attempts
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
if (!verifyOtp(otp, otpHash)) {
|
|
200
|
+
await req.payload.update({
|
|
201
|
+
collection: 'auth-otps',
|
|
202
|
+
id: otpDoc.id,
|
|
203
|
+
data: {
|
|
204
|
+
attempts: (otpDoc.attempts || 0) + 1
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
if (!verifyOtp(otp, otpDoc.hash)) {
|
|
227
208
|
return Response.json({
|
|
228
209
|
success: false,
|
|
229
210
|
error: 'Invalid code. Please try again.'
|
|
@@ -231,35 +212,64 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
231
212
|
status: 400
|
|
232
213
|
});
|
|
233
214
|
}
|
|
234
|
-
// OTP is valid —
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
215
|
+
// OTP is valid — delete it
|
|
216
|
+
await req.payload.delete({
|
|
217
|
+
collection: 'auth-otps',
|
|
218
|
+
id: otpDoc.id
|
|
219
|
+
});
|
|
220
|
+
// Find the user
|
|
221
|
+
const users = await req.payload.find({
|
|
239
222
|
collection: 'users',
|
|
240
|
-
|
|
241
|
-
email:
|
|
242
|
-
|
|
223
|
+
where: {
|
|
224
|
+
email: {
|
|
225
|
+
equals: email.toLowerCase().trim()
|
|
226
|
+
}
|
|
243
227
|
},
|
|
244
|
-
|
|
228
|
+
limit: 1
|
|
245
229
|
});
|
|
246
|
-
|
|
247
|
-
if (user
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
otpPurpose: null,
|
|
254
|
-
otpAttempts: null,
|
|
255
|
-
otpExpiresAt: null
|
|
256
|
-
}
|
|
230
|
+
const user = users.docs?.[0];
|
|
231
|
+
if (!user) {
|
|
232
|
+
return Response.json({
|
|
233
|
+
success: false,
|
|
234
|
+
error: 'User not found'
|
|
235
|
+
}, {
|
|
236
|
+
status: 404
|
|
257
237
|
});
|
|
258
238
|
}
|
|
259
|
-
|
|
239
|
+
// Use temp password strategy (same as PaintPulse) — only touches password field
|
|
240
|
+
const tempPassword = crypto.randomBytes(32).toString('hex');
|
|
241
|
+
await req.payload.update({
|
|
242
|
+
collection: 'users',
|
|
243
|
+
id: user.id,
|
|
244
|
+
data: {
|
|
245
|
+
password: tempPassword
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
// Login with the temp password to get a proper JWT token
|
|
249
|
+
const loginResult = await req.payload.login({
|
|
250
|
+
collection: 'users',
|
|
251
|
+
data: {
|
|
252
|
+
email: email.toLowerCase().trim(),
|
|
253
|
+
password: tempPassword
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
if (!loginResult.user) {
|
|
257
|
+
throw new Error('Login failed after OTP verification');
|
|
258
|
+
}
|
|
259
|
+
// Set the auth cookie directly on the response
|
|
260
|
+
const response = Response.json({
|
|
260
261
|
success: true,
|
|
261
|
-
token:
|
|
262
|
-
isNewUser: !user?.
|
|
262
|
+
token: loginResult.token,
|
|
263
|
+
isNewUser: !user?.hasPassword
|
|
264
|
+
});
|
|
265
|
+
// Attach payload-token cookie
|
|
266
|
+
const cookieHeader = `payload-token=${loginResult.token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${7200}`;
|
|
267
|
+
return new Response(response.body, {
|
|
268
|
+
status: 200,
|
|
269
|
+
headers: {
|
|
270
|
+
'Content-Type': 'application/json',
|
|
271
|
+
'Set-Cookie': cookieHeader
|
|
272
|
+
}
|
|
263
273
|
});
|
|
264
274
|
} catch (err) {
|
|
265
275
|
console.error('OTP verify error:', err);
|
|
@@ -275,7 +285,7 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
275
285
|
/**
|
|
276
286
|
* POST /api/auth/set-password — Set/update password for authenticated user
|
|
277
287
|
*/ export const setPasswordEndpoint = {
|
|
278
|
-
path: '/
|
|
288
|
+
path: '/auth/set-password',
|
|
279
289
|
method: 'post',
|
|
280
290
|
handler: async (req)=>{
|
|
281
291
|
const body = req.json ? await req.json() : req.body || {};
|
|
@@ -339,9 +349,17 @@ import { generateWelcomeEmail, generateOtpEmail } from '../components/email/inde
|
|
|
339
349
|
/**
|
|
340
350
|
* POST /api/auth/signup — Create new user + send welcome email
|
|
341
351
|
*/ export const signupEndpoint = {
|
|
342
|
-
path: '/
|
|
352
|
+
path: '/auth/signup',
|
|
343
353
|
method: 'post',
|
|
344
354
|
handler: async (req)=>{
|
|
355
|
+
if (process.env.AUTH_PLUGIN_ALLOW_SIGNUP === 'false') {
|
|
356
|
+
return Response.json({
|
|
357
|
+
success: false,
|
|
358
|
+
message: 'Signups are currently disabled'
|
|
359
|
+
}, {
|
|
360
|
+
status: 403
|
|
361
|
+
});
|
|
362
|
+
}
|
|
345
363
|
const body = req.json ? await req.json() : req.body || {};
|
|
346
364
|
const { name, email } = body;
|
|
347
365
|
if (!email) {
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { initClientConfig } from '../config';
|
|
2
|
+
export { AuthClientInit } from '../components/AuthClientInit';
|
|
3
|
+
export type { AuthClientInitProps } from '../components/AuthClientInit';
|
|
2
4
|
export { useLoginFlow } from '../auth/application/hooks/useLoginFlow';
|
|
3
5
|
export { useVerifyOtpFlow } from '../auth/application/hooks/useVerifyOtpFlow';
|
|
4
6
|
export { useForgotPasswordFlow } from '../auth/application/hooks/useForgotPasswordFlow';
|
package/dist/exports/client.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// ============================================================
|
|
5
5
|
// Plugin client config initializer
|
|
6
6
|
export { initClientConfig } from '../config.js';
|
|
7
|
+
export { AuthClientInit } from '../components/AuthClientInit.js';
|
|
7
8
|
// Auth hooks
|
|
8
9
|
export { useLoginFlow } from '../auth/application/hooks/useLoginFlow.js';
|
|
9
10
|
export { useVerifyOtpFlow } from '../auth/application/hooks/useVerifyOtpFlow.js';
|
package/dist/exports/rsc.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { default as AuthPages } from '../components/AuthPagesServer';
|
|
2
|
+
export type { AuthPagesProps } from '../components/AuthPagesServer';
|
|
1
3
|
export { wrapInBaseTemplate, DEFAULT_COLORS, SOCIAL_ICONS, getBaseUrl, getSenderEmail, getEmailTranslations, generateOtpEmail, generateWelcomeEmail, generatePasswordResetEmail, generatePasswordChangedEmail, } from '../components/email/index';
|
|
2
4
|
export type { BaseTemplateOptions, } from '../components/email/baseTemplate';
|
|
3
5
|
export type { EmailColors, SocialLink, SocialPlatform, } from '../components/email/constants';
|
package/dist/exports/rsc.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// ============================================================
|
|
2
2
|
// RSC (React Server Components) exports for @main12/auth-login/rsc
|
|
3
3
|
// ============================================================
|
|
4
|
+
// Server component auth pages (reads plugin config automatically)
|
|
5
|
+
export { default as AuthPages } from '../components/AuthPagesServer.js';
|
|
4
6
|
// Email template system (server-only — uses Node APIs)
|
|
5
7
|
export { wrapInBaseTemplate, DEFAULT_COLORS, SOCIAL_ICONS, getBaseUrl, getSenderEmail, getEmailTranslations, generateOtpEmail, generateWelcomeEmail, generatePasswordResetEmail, generatePasswordChangedEmail } from '../components/email/index.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface GoogleOAuthConfig {
|
|
|
7
7
|
clientSecret?: string;
|
|
8
8
|
/** Toggle provider on/off */
|
|
9
9
|
enabled?: boolean;
|
|
10
|
+
/** Where to redirect after successful Google login. Defaults to '/admin' */
|
|
11
|
+
successRedirect?: string;
|
|
12
|
+
/** Where to redirect after failed Google login. Defaults to '/login?error=Google login failed' */
|
|
13
|
+
failureRedirect?: string;
|
|
10
14
|
}
|
|
11
15
|
export interface AuthProvidersConfig {
|
|
12
16
|
google?: GoogleOAuthConfig | boolean;
|
|
@@ -40,6 +44,28 @@ export interface AuthLoginPluginOptions {
|
|
|
40
44
|
routeRedirects?: boolean | {
|
|
41
45
|
basePath?: string;
|
|
42
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Allow new users to sign up.
|
|
49
|
+
* When false, signup page is hidden, signup link is removed from login,
|
|
50
|
+
* and Google OAuth rejects unregistered users.
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
allowSignup?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Allow login via password.
|
|
56
|
+
* When true, users with passwords see the password step.
|
|
57
|
+
* When false, all logins go through OTP.
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
passwordLogin?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Allow login via OTP (email verification code).
|
|
63
|
+
* When true, users without passwords get OTP automatically,
|
|
64
|
+
* and after OTP verification they go straight to the app.
|
|
65
|
+
* When false, users are prompted to set a password after OTP.
|
|
66
|
+
* @default true
|
|
67
|
+
*/
|
|
68
|
+
otpLogin?: boolean;
|
|
43
69
|
}
|
|
44
|
-
export declare const authLoginPlugin: (options?: AuthLoginPluginOptions) => (config: Config) => Config
|
|
70
|
+
export declare const authLoginPlugin: (options?: AuthLoginPluginOptions) => (config: Config) => Promise<Config>;
|
|
45
71
|
export { pluginConfig };
|