@main12/auth-login 0.2.3 → 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 +9 -1
- package/dist/components/AuthPages.js +22 -3
- 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 +6 -2
- 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 +10 -2
- package/package.json +8 -2
- package/dist/endpoints/googleOAuth.d.ts +0 -10
- package/dist/endpoints/googleOAuth.js +0 -144
package/README.md
CHANGED
|
@@ -10,8 +10,9 @@ plugins: [
|
|
|
10
10
|
authLoginPlugin({
|
|
11
11
|
projectName: 'My SaaS',
|
|
12
12
|
domain: 'https://myapp.com',
|
|
13
|
-
logo: 'https://myapp.com/logo.png',
|
|
14
|
-
style: '
|
|
13
|
+
logo: 'https://myapp.com/logo.png',
|
|
14
|
+
style: 'tailwind',
|
|
15
|
+
routeRedirects: true,
|
|
15
16
|
}),
|
|
16
17
|
]
|
|
17
18
|
```
|
|
@@ -21,7 +22,10 @@ plugins: [
|
|
|
21
22
|
## Features
|
|
22
23
|
|
|
23
24
|
- **5 auth pages** — login (multi-step), signup, forgot password, verify OTP, set password
|
|
25
|
+
- **Single catch-all route** — one file handles all auth pages (`AuthPages` component)
|
|
26
|
+
- **Route redirects** — automatic `/login` → `/auth/login`, `/admin/login` → `/auth/login` via Next.js 16 proxy
|
|
24
27
|
- **Multi-style** — `tailwind` (zero UI deps) or `hero-ui` (HeroUI + framer-motion). Set once in config
|
|
28
|
+
- **Google OAuth** — optional, enable via `providers` config (hidden by default)
|
|
25
29
|
- **5 API endpoints** — `check-email`, `otp/send`, `otp/verify`, `set-password`, `signup`
|
|
26
30
|
- **OTP engine** — SHA-256 hashing + `timingSafeEqual` comparison, 10-min expiry, 3 attempts
|
|
27
31
|
- **Email templates** — welcome, OTP login, password reset, password changed (EN/ES)
|
|
@@ -49,11 +53,14 @@ pnpm add @heroui/react framer-motion @iconify/react
|
|
|
49
53
|
|
|
50
54
|
---
|
|
51
55
|
|
|
52
|
-
## Quick Start
|
|
56
|
+
## Quick Start (Simplified Setup)
|
|
57
|
+
|
|
58
|
+
The fastest way to get all auth pages running with just **2 files**.
|
|
53
59
|
|
|
54
60
|
### 1. Add the plugin + Users collection
|
|
55
61
|
|
|
56
62
|
```ts
|
|
63
|
+
// payload.config.ts
|
|
57
64
|
import { authLoginPlugin } from '@main12/auth-login'
|
|
58
65
|
|
|
59
66
|
export default buildConfig({
|
|
@@ -74,45 +81,50 @@ export default buildConfig({
|
|
|
74
81
|
projectName: 'My App',
|
|
75
82
|
domain: 'https://myapp.com',
|
|
76
83
|
logo: '/logo.png',
|
|
77
|
-
style: '
|
|
84
|
+
style: 'tailwind',
|
|
85
|
+
routeRedirects: true, // enables /login → /auth/login redirects
|
|
78
86
|
}),
|
|
79
87
|
],
|
|
80
88
|
})
|
|
81
89
|
```
|
|
82
90
|
|
|
83
|
-
### 2. Create
|
|
91
|
+
### 2. Create the catch-all auth route (1 file)
|
|
84
92
|
|
|
85
93
|
```tsx
|
|
86
|
-
// src/app/(frontend)/(auth)/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// src/app/(frontend)/(auth)/signup/page.tsx
|
|
90
|
-
export { SignupPage as default } from '@main12/auth-login/client'
|
|
91
|
-
|
|
92
|
-
// src/app/(frontend)/(auth)/forgot-password/page.tsx
|
|
93
|
-
export { ForgotPasswordPage as default } from '@main12/auth-login/client'
|
|
94
|
+
// src/app/(frontend)/(auth)/auth/[...slug]/page.tsx
|
|
95
|
+
'use client'
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
import { use } from 'react'
|
|
98
|
+
import { AuthPages } from '@main12/auth-login/client'
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
export default function Page({ params }: { params: Promise<{ slug: string[] }> }) {
|
|
101
|
+
const { slug } = use(params)
|
|
102
|
+
return <AuthPages slug={slug} />
|
|
103
|
+
}
|
|
100
104
|
```
|
|
101
105
|
|
|
102
|
-
|
|
106
|
+
This single file handles all routes:
|
|
107
|
+
- `/auth/login`
|
|
108
|
+
- `/auth/signup`
|
|
109
|
+
- `/auth/forgot-password`
|
|
110
|
+
- `/auth/verify-otp`
|
|
111
|
+
- `/auth/set-password`
|
|
103
112
|
|
|
104
|
-
|
|
105
|
-
// login/page.tsx
|
|
106
|
-
'use client'
|
|
107
|
-
import { LoginPage } from '@main12/auth-login/client'
|
|
108
|
-
import { useAuth } from '@/providers/Auth'
|
|
113
|
+
### 3. Add the proxy for route redirects (1 file)
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
115
|
+
```ts
|
|
116
|
+
// src/proxy.ts (Next.js 16+)
|
|
117
|
+
export { proxy, config } from '@main12/auth-login/proxy'
|
|
114
118
|
```
|
|
115
119
|
|
|
120
|
+
This redirects:
|
|
121
|
+
- `/login` → `/auth/login`
|
|
122
|
+
- `/signup` → `/auth/signup`
|
|
123
|
+
- `/forgot-password` → `/auth/forgot-password`
|
|
124
|
+
- `/verify-otp` → `/auth/verify-otp`
|
|
125
|
+
- `/set-password` → `/auth/set-password`
|
|
126
|
+
- `/admin/login` → `/auth/login` (**always**, regardless of `routeRedirects` setting)
|
|
127
|
+
|
|
116
128
|
### 4. Visit `/login` — done.
|
|
117
129
|
|
|
118
130
|
---
|
|
@@ -121,26 +133,127 @@ export default function Page() {
|
|
|
121
133
|
|
|
122
134
|
```ts
|
|
123
135
|
authLoginPlugin({
|
|
136
|
+
// Core
|
|
124
137
|
projectName: 'My App', // Email subjects + footers
|
|
125
138
|
contactEmail: 'hi@myapp.com', // Email footer contact
|
|
126
139
|
domain: 'https://myapp.com', // Links in emails
|
|
127
140
|
logo: '/logo.png', // All auth pages + email headers
|
|
128
141
|
style: 'tailwind', // 'tailwind' | 'hero-ui'
|
|
129
142
|
enabled: true,
|
|
143
|
+
|
|
144
|
+
// Route redirects
|
|
145
|
+
routeRedirects: true, // Enable /login → /auth/login redirects
|
|
146
|
+
// or with custom base path:
|
|
147
|
+
// routeRedirects: { basePath: '/auth' },
|
|
148
|
+
|
|
149
|
+
// OAuth providers (optional — hidden by default)
|
|
150
|
+
providers: {
|
|
151
|
+
google: {
|
|
152
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
153
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Route Redirects
|
|
160
|
+
|
|
161
|
+
| Value | Behavior |
|
|
162
|
+
|-------|----------|
|
|
163
|
+
| `false` (default) | No redirects. You manage your own routes. |
|
|
164
|
+
| `true` | Redirects `/login`, `/signup`, etc. → `/auth/login`, `/auth/signup`, etc. |
|
|
165
|
+
| `{ basePath: '/myauth' }` | Same, but redirects to `/myauth/login`, etc. |
|
|
166
|
+
|
|
167
|
+
> **Note:** `/admin/login` is **always** redirected to the plugin login page when the proxy is active, regardless of the `routeRedirects` setting.
|
|
168
|
+
|
|
169
|
+
### Google OAuth (Providers)
|
|
170
|
+
|
|
171
|
+
Google OAuth is **hidden by default**. To enable it, just add `providers.google` — the plugin handles everything (UI buttons + server-side OAuth flow via `payload-oauth2` under the hood):
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
authLoginPlugin({
|
|
175
|
+
providers: {
|
|
176
|
+
google: {
|
|
177
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
178
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
179
|
+
successRedirect: '/dashboard', // optional, defaults to '/admin'
|
|
180
|
+
failureRedirect: '/login?error=failed', // optional
|
|
181
|
+
},
|
|
182
|
+
},
|
|
130
183
|
})
|
|
131
184
|
```
|
|
132
185
|
|
|
186
|
+
That's it. No extra packages to install, no extra plugins to configure.
|
|
187
|
+
|
|
188
|
+
| Config | Behavior |
|
|
189
|
+
|--------|----------|
|
|
190
|
+
| Omitted / not set | Google OAuth hidden |
|
|
191
|
+
| `google: true` | Auto-detect `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` from env |
|
|
192
|
+
| `google: { clientId, clientSecret }` | Enable with explicit credentials |
|
|
193
|
+
| `google: false` | Force disable |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## AuthPages Props
|
|
198
|
+
|
|
199
|
+
The `AuthPages` component accepts these props for customization:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
<AuthPages
|
|
203
|
+
slug={slug} // Required — from catch-all route params
|
|
204
|
+
redirectTo="/dashboard" // Where to go after login/set-password (default: '/admin')
|
|
205
|
+
logo={<MyLogo />} // Custom logo component
|
|
206
|
+
basePath="/auth" // Base path for sibling links (default: '/auth')
|
|
207
|
+
showGoogleOAuth={true} // Override Google OAuth visibility
|
|
208
|
+
onPasswordLogin={customLogin} // Custom login handler
|
|
209
|
+
onSignup={customSignup} // Custom signup handler
|
|
210
|
+
/>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Individual Page Setup (Advanced)
|
|
216
|
+
|
|
217
|
+
If you need full control over each page, create separate route files instead of using `AuthPages`:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// login/page.tsx
|
|
221
|
+
'use client'
|
|
222
|
+
import { LoginPage } from '@main12/auth-login/client'
|
|
223
|
+
|
|
224
|
+
export default function Page() {
|
|
225
|
+
return (
|
|
226
|
+
<LoginPage
|
|
227
|
+
onPasswordLogin={async ({ email, password }) => {
|
|
228
|
+
const res = await fetch('/api/users/login', {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
body: JSON.stringify({ email, password }),
|
|
232
|
+
})
|
|
233
|
+
if (!res.ok) throw new Error('Login failed')
|
|
234
|
+
}}
|
|
235
|
+
redirectTo="/dashboard"
|
|
236
|
+
signupUrl="/signup"
|
|
237
|
+
/>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
133
242
|
### Per-page overrides
|
|
134
243
|
|
|
135
244
|
```tsx
|
|
136
245
|
<LoginPage
|
|
137
|
-
logo={<AppLogo width={180} />}
|
|
138
|
-
onPasswordLogin={login}
|
|
246
|
+
logo={<AppLogo width={180} />}
|
|
247
|
+
onPasswordLogin={login}
|
|
139
248
|
redirectTo="/dashboard"
|
|
140
249
|
showGoogleOAuth={true}
|
|
141
250
|
signupUrl="/signup"
|
|
142
|
-
poweredBy={{ enabled: true, logoUrl: '/custom.png', linkUrl: 'https://...' }}
|
|
143
251
|
/>
|
|
252
|
+
|
|
253
|
+
<SignupPage onSignup={signup} loginUrl="/login" />
|
|
254
|
+
<ForgotPasswordPage loginUrl="/login" />
|
|
255
|
+
<VerifyOtpPage loginUrl="/login" />
|
|
256
|
+
<SetPasswordPage redirectTo="/dashboard" />
|
|
144
257
|
```
|
|
145
258
|
|
|
146
259
|
---
|
|
@@ -289,17 +402,39 @@ await payload.sendEmail({
|
|
|
289
402
|
|
|
290
403
|
---
|
|
291
404
|
|
|
405
|
+
## Exports
|
|
406
|
+
|
|
407
|
+
| Import path | Contents |
|
|
408
|
+
|-------------|----------|
|
|
409
|
+
| `@main12/auth-login` | Plugin factory, types, server config |
|
|
410
|
+
| `@main12/auth-login/client` | Page components, `AuthPages`, hooks, services, UI utilities |
|
|
411
|
+
| `@main12/auth-login/rsc` | Email template generators, translations |
|
|
412
|
+
| `@main12/auth-login/proxy` | Next.js 16 proxy for route redirects |
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
292
416
|
## ShadCN Compatibility
|
|
293
417
|
|
|
294
418
|
The `tailwind` style works in ShadCN projects out of the box. For ShadCN components, build a [custom page](#building-custom-pages) — import the plugin's hooks and use your `@/components/ui/button`, `@/components/ui/input`, etc.
|
|
295
419
|
|
|
296
420
|
---
|
|
297
421
|
|
|
422
|
+
## Setup Comparison
|
|
423
|
+
|
|
424
|
+
| Approach | Files needed | Best for |
|
|
425
|
+
|----------|-------------|----------|
|
|
426
|
+
| **Catch-all + proxy** (recommended) | 2 files | Most projects — fastest setup |
|
|
427
|
+
| **Individual pages** | 5 files | Full control over each page |
|
|
428
|
+
| **Custom pages with hooks** | Your own files | Completely custom UI |
|
|
429
|
+
| **Backend only** (`routeRedirects: false`, no `AuthPages`) | 0 frontend files | Custom frontend using only the API endpoints + hooks |
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
298
433
|
## 🤖 AI Agent Prompts
|
|
299
434
|
|
|
300
435
|
Copy these prompts into Claude, Cursor, Copilot, or any AI agent.
|
|
301
436
|
|
|
302
|
-
### Prompt: Set up the auth plugin
|
|
437
|
+
### Prompt: Set up the auth plugin (simplified catch-all)
|
|
303
438
|
|
|
304
439
|
```
|
|
305
440
|
Add @main12/auth-login to this Payload project:
|
|
@@ -307,15 +442,38 @@ Add @main12/auth-login to this Payload project:
|
|
|
307
442
|
1. Install: pnpm add @main12/auth-login
|
|
308
443
|
2. In payload.config.ts, add:
|
|
309
444
|
- Users collection with auth enabled + otpHash, otpAttempts, otpExpiresAt fields
|
|
310
|
-
- Plugin: authLoginPlugin({ projectName: "<PROJECT>", domain: "<URL>", logo: "/logo.png", style: "
|
|
311
|
-
3. Create route
|
|
312
|
-
-
|
|
313
|
-
-
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
445
|
+
- Plugin: authLoginPlugin({ projectName: "<PROJECT>", domain: "<URL>", logo: "/logo.png", style: "tailwind", routeRedirects: true })
|
|
446
|
+
3. Create catch-all route: src/app/(frontend)/(auth)/auth/[...slug]/page.tsx
|
|
447
|
+
- 'use client', import { use } from 'react', import { AuthPages } from '@main12/auth-login/client'
|
|
448
|
+
- export default function Page({ params }) { const { slug } = use(params); return <AuthPages slug={slug} /> }
|
|
449
|
+
4. Create proxy: src/proxy.ts
|
|
450
|
+
- export { proxy, config } from '@main12/auth-login/proxy'
|
|
451
|
+
5. Verify: visit /login → should redirect to /auth/login
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Prompt: Set up with Google OAuth
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
Add @main12/auth-login with Google OAuth to this Payload project:
|
|
458
|
+
|
|
459
|
+
1. Install: pnpm add @main12/auth-login
|
|
460
|
+
2. In payload.config.ts, add:
|
|
461
|
+
- Users collection with auth enabled + otpHash, otpAttempts, otpExpiresAt fields
|
|
462
|
+
- Plugin: authLoginPlugin({
|
|
463
|
+
projectName: "<PROJECT>",
|
|
464
|
+
domain: "<URL>",
|
|
465
|
+
style: "tailwind",
|
|
466
|
+
routeRedirects: true,
|
|
467
|
+
providers: {
|
|
468
|
+
google: {
|
|
469
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
470
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
3. Create catch-all route + proxy (same as simplified setup)
|
|
475
|
+
4. Add GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to .env
|
|
476
|
+
5. Verify: visit /login → should show Google OAuth button
|
|
319
477
|
```
|
|
320
478
|
|
|
321
479
|
### Prompt: Build a custom login page with HeroUI components
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { useState, useCallback } from 'react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { checkEmail, sendOtp, initiateGoogleLogin } from '../services/authService.js';
|
|
5
|
+
import { pluginConfig } from '../../../config.js';
|
|
5
6
|
/**
|
|
6
7
|
* State machine for the multi-step login flow: email → password | otp-prompt.
|
|
7
8
|
* The page component owns the UI; this hook owns the logic.
|
|
@@ -25,14 +26,29 @@ import { checkEmail, sendOtp, initiateGoogleLogin } from '../services/authServic
|
|
|
25
26
|
setError('noAccountFound');
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
const { passwordLogin: passwordLoginEnabled, otpLogin: otpLoginEnabled } = pluginConfig;
|
|
30
|
+
if (data.hasPassword && passwordLoginEnabled) {
|
|
31
|
+
// Show password step if user has a password and password login is enabled
|
|
32
|
+
setStep('password');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// OTP login — send OTP immediately and redirect to verify page
|
|
36
|
+
const otpData = await sendOtp(email, 'login');
|
|
37
|
+
if (otpData.success) {
|
|
38
|
+
const redirectParam = redirectTo !== '/' ? `&redirect=${encodeURIComponent(redirectTo)}` : '';
|
|
39
|
+
router.push(`/verify-otp?email=${encodeURIComponent(email.trim())}${redirectParam}`);
|
|
40
|
+
} else {
|
|
41
|
+
setError(otpData.message || 'otpSendFailed');
|
|
42
|
+
}
|
|
29
43
|
} catch {
|
|
30
44
|
setError('genericError');
|
|
31
45
|
} finally{
|
|
32
46
|
setIsLoading(false);
|
|
33
47
|
}
|
|
34
48
|
}, [
|
|
35
|
-
email
|
|
49
|
+
email,
|
|
50
|
+
redirectTo,
|
|
51
|
+
router
|
|
36
52
|
]);
|
|
37
53
|
const handlePasswordSubmit = useCallback(async (e)=>{
|
|
38
54
|
e.preventDefault();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { useState, useCallback, useEffect } from 'react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { verifyOtp, sendOtp } from '../services/authService.js';
|
|
5
|
+
import { pluginConfig } from '../../../config.js';
|
|
5
6
|
/**
|
|
6
7
|
* State machine for OTP verification: input → verify → redirect | resend.
|
|
7
8
|
*/ export function useVerifyOtpFlow({ email, purpose, redirectTo = '/' }) {
|
|
@@ -10,7 +11,8 @@ import { verifyOtp, sendOtp } from '../services/authService.js';
|
|
|
10
11
|
const [isLoading, setIsLoading] = useState(false);
|
|
11
12
|
const [error, setError] = useState(null);
|
|
12
13
|
const [isResending, setIsResending] = useState(false);
|
|
13
|
-
|
|
14
|
+
// Start with 30s cooldown since a code was just sent before arriving here
|
|
15
|
+
const [resendCooldown, setResendCooldown] = useState(30);
|
|
14
16
|
useEffect(()=>{
|
|
15
17
|
if (resendCooldown > 0) {
|
|
16
18
|
const timer = setTimeout(()=>setResendCooldown((c)=>c - 1), 1000);
|
|
@@ -26,11 +28,15 @@ import { verifyOtp, sendOtp } from '../services/authService.js';
|
|
|
26
28
|
try {
|
|
27
29
|
const data = await verifyOtp(email, otp);
|
|
28
30
|
if (data.success) {
|
|
31
|
+
const { passwordLogin: passwordLoginEnabled, otpLogin: otpLoginEnabled } = pluginConfig;
|
|
29
32
|
if (purpose === 'password-reset') {
|
|
33
|
+
// Always allow setting password from explicit password-reset flow
|
|
30
34
|
router.push(`/set-password?redirect=${encodeURIComponent(redirectTo)}`);
|
|
31
|
-
} else if (data.isNewUser) {
|
|
35
|
+
} else if (!otpLoginEnabled && passwordLoginEnabled && data.isNewUser) {
|
|
36
|
+
// Only prompt for password if OTP login is disabled and password login is enabled
|
|
32
37
|
router.push(`/set-password?redirect=${encodeURIComponent(redirectTo)}`);
|
|
33
38
|
} else {
|
|
39
|
+
// OTP login is enabled — go straight to the app
|
|
34
40
|
window.location.href = redirectTo;
|
|
35
41
|
}
|
|
36
42
|
} else {
|
|
@@ -22,5 +22,6 @@ export declare function setUserPassword(password: string, confirmPassword: strin
|
|
|
22
22
|
export declare function signup(name: string, email: string): Promise<SignupResponse>;
|
|
23
23
|
/**
|
|
24
24
|
* Redirect the browser to the Google OAuth login endpoint.
|
|
25
|
+
* Uses payload-oauth2 plugin's route at /api/users/oauth/google.
|
|
25
26
|
*/
|
|
26
27
|
export declare function initiateGoogleLogin(redirectTo?: string): void;
|
|
@@ -80,10 +80,11 @@ const API_PREFIX = '/api/auth';
|
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* Redirect the browser to the Google OAuth login endpoint.
|
|
83
|
+
* Uses payload-oauth2 plugin's route at /api/users/oauth/google.
|
|
83
84
|
*/ export function initiateGoogleLogin(redirectTo = '/') {
|
|
84
85
|
const params = new URLSearchParams();
|
|
85
86
|
if (redirectTo !== '/') {
|
|
86
|
-
params.set('
|
|
87
|
+
params.set('state', redirectTo);
|
|
87
88
|
}
|
|
88
89
|
const qs = params.toString();
|
|
89
90
|
window.location.href = `/api/users/oauth/google${qs ? `?${qs}` : ''}`;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type AuthStyle } from '../config';
|
|
2
|
+
export interface AuthClientInitProps {
|
|
3
|
+
/** Override UI style. If omitted, reads from plugin config. */
|
|
4
|
+
style?: AuthStyle;
|
|
5
|
+
/** Override Google OAuth visibility. If omitted, reads from plugin config. */
|
|
6
|
+
googleOAuthEnabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Drop this component into your root layout to sync the server-side
|
|
10
|
+
* plugin config to client components. No props needed — it reads
|
|
11
|
+
* everything from the plugin configuration automatically.
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // app/(frontend)/layout.tsx
|
|
15
|
+
* import { AuthClientInit } from '@main12/auth-login/client'
|
|
16
|
+
*
|
|
17
|
+
* export default function Layout({ children }) {
|
|
18
|
+
* return (
|
|
19
|
+
* <html><body>
|
|
20
|
+
* <AuthClientInit />
|
|
21
|
+
* {children}
|
|
22
|
+
* </body></html>
|
|
23
|
+
* )
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function AuthClientInit({ style, googleOAuthEnabled }?: AuthClientInitProps): null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { pluginConfig, initClientConfig } from '../config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Drop this component into your root layout to sync the server-side
|
|
5
|
+
* plugin config to client components. No props needed — it reads
|
|
6
|
+
* everything from the plugin configuration automatically.
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/(frontend)/layout.tsx
|
|
10
|
+
* import { AuthClientInit } from '@main12/auth-login/client'
|
|
11
|
+
*
|
|
12
|
+
* export default function Layout({ children }) {
|
|
13
|
+
* return (
|
|
14
|
+
* <html><body>
|
|
15
|
+
* <AuthClientInit />
|
|
16
|
+
* {children}
|
|
17
|
+
* </body></html>
|
|
18
|
+
* )
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/ export function AuthClientInit({ style, googleOAuthEnabled } = {}) {
|
|
22
|
+
initClientConfig({
|
|
23
|
+
style: style ?? pluginConfig.style,
|
|
24
|
+
googleOAuthEnabled: googleOAuthEnabled ?? pluginConfig.googleOAuthEnabled,
|
|
25
|
+
passwordLogin: pluginConfig.passwordLogin,
|
|
26
|
+
otpLogin: pluginConfig.otpLogin
|
|
27
|
+
});
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
@@ -18,5 +18,13 @@ export interface AuthPagesProps {
|
|
|
18
18
|
}) => Promise<void>;
|
|
19
19
|
/** Base path prefix, defaults to '/auth'. Used to compute sibling URLs. */
|
|
20
20
|
basePath?: string;
|
|
21
|
+
/** Show Google OAuth buttons. If omitted, reads from plugin config. */
|
|
22
|
+
showGoogleOAuth?: boolean;
|
|
23
|
+
/** Override the page background. Defaults to 'bg-white md:bg-[#191919]'. */
|
|
24
|
+
backgroundClass?: string;
|
|
25
|
+
/** UI style override. If omitted, reads from plugin config. */
|
|
26
|
+
style?: 'tailwind' | 'hero-ui';
|
|
27
|
+
/** Allow new user signups. When false, hides signup page and signup links. Defaults to true. */
|
|
28
|
+
allowSignup?: boolean;
|
|
21
29
|
}
|
|
22
|
-
export default function AuthPages({ slug, redirectTo, logo, onPasswordLogin, onSignup, basePath, }: AuthPagesProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export default function AuthPages({ slug, redirectTo, logo, onPasswordLogin, onSignup, basePath, showGoogleOAuth, backgroundClass, style, allowSignup, }: AuthPagesProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { pluginConfig, initClientConfig } from '../config.js';
|
|
3
4
|
import LoginPage from './pages/LoginPage.js';
|
|
4
5
|
import SignupPage from './pages/SignupPage.js';
|
|
5
6
|
import ForgotPasswordPage from './pages/ForgotPasswordPage.js';
|
|
@@ -37,21 +38,39 @@ const defaultSignup = async ({ name, email })=>{
|
|
|
37
38
|
throw new Error(err.message || 'Signup failed');
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
|
-
export default function AuthPages({ slug, redirectTo = '/admin', logo, onPasswordLogin = defaultPasswordLogin, onSignup = defaultSignup, basePath = '/auth' }) {
|
|
41
|
+
export default function AuthPages({ slug, redirectTo = '/admin', logo, onPasswordLogin = defaultPasswordLogin, onSignup = defaultSignup, basePath = '/auth', showGoogleOAuth, backgroundClass, style, allowSignup = true }) {
|
|
42
|
+
// Sync server plugin config to client using props (which come from the server via AuthPagesServer)
|
|
43
|
+
initClientConfig({
|
|
44
|
+
style: style ?? pluginConfig.style,
|
|
45
|
+
googleOAuthEnabled: showGoogleOAuth ?? pluginConfig.googleOAuthEnabled
|
|
46
|
+
});
|
|
41
47
|
const page = slug?.[0] ?? 'login';
|
|
42
48
|
const base = basePath.replace(/\/$/, '');
|
|
43
49
|
const shared = {
|
|
44
|
-
logo
|
|
50
|
+
logo,
|
|
51
|
+
...showGoogleOAuth !== undefined ? {
|
|
52
|
+
showGoogleOAuth
|
|
53
|
+
} : {},
|
|
54
|
+
...backgroundClass !== undefined ? {
|
|
55
|
+
backgroundClass
|
|
56
|
+
} : {}
|
|
45
57
|
};
|
|
46
58
|
switch(page){
|
|
47
59
|
case 'login':
|
|
48
60
|
return /*#__PURE__*/ _jsx(LoginPage, {
|
|
49
61
|
onPasswordLogin: onPasswordLogin,
|
|
50
62
|
redirectTo: redirectTo,
|
|
51
|
-
signupUrl: `${base}/signup
|
|
63
|
+
signupUrl: allowSignup ? `${base}/signup` : undefined,
|
|
52
64
|
...shared
|
|
53
65
|
});
|
|
54
66
|
case 'signup':
|
|
67
|
+
if (!allowSignup) {
|
|
68
|
+
return /*#__PURE__*/ _jsx(LoginPage, {
|
|
69
|
+
onPasswordLogin: onPasswordLogin,
|
|
70
|
+
redirectTo: redirectTo,
|
|
71
|
+
...shared
|
|
72
|
+
});
|
|
73
|
+
}
|
|
55
74
|
return /*#__PURE__*/ _jsx(SignupPage, {
|
|
56
75
|
onSignup: onSignup,
|
|
57
76
|
loginUrl: `${base}/login`,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AuthPagesProps } from './AuthPages';
|
|
2
|
+
export type { AuthPagesProps };
|
|
3
|
+
/**
|
|
4
|
+
* Server component wrapper for AuthPages.
|
|
5
|
+
* Reads plugin config via env vars (set by the plugin at init time)
|
|
6
|
+
* and passes it to the client component automatically.
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/(auth)/auth/[...slug]/page.tsx (NO 'use client' needed!)
|
|
10
|
+
* import { AuthPages } from '@main12/auth-login/rsc'
|
|
11
|
+
* export default async function Page({ params }) {
|
|
12
|
+
* const { slug } = await params
|
|
13
|
+
* return <AuthPages slug={slug} />
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export default function AuthPages(props: AuthPagesProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import AuthPagesClient from './AuthPages.js';
|
|
3
|
+
/**
|
|
4
|
+
* Server component wrapper for AuthPages.
|
|
5
|
+
* Reads plugin config via env vars (set by the plugin at init time)
|
|
6
|
+
* and passes it to the client component automatically.
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/(auth)/auth/[...slug]/page.tsx (NO 'use client' needed!)
|
|
10
|
+
* import { AuthPages } from '@main12/auth-login/rsc'
|
|
11
|
+
* export default async function Page({ params }) {
|
|
12
|
+
* const { slug } = await params
|
|
13
|
+
* return <AuthPages slug={slug} />
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/ export default function AuthPages(props) {
|
|
17
|
+
const googleOAuthEnabled = process.env.AUTH_PLUGIN_GOOGLE_OAUTH === 'true';
|
|
18
|
+
const style = process.env.AUTH_PLUGIN_STYLE || 'tailwind';
|
|
19
|
+
const allowSignup = process.env.AUTH_PLUGIN_ALLOW_SIGNUP !== 'false';
|
|
20
|
+
return /*#__PURE__*/ _jsx(AuthPagesClient, {
|
|
21
|
+
...props,
|
|
22
|
+
showGoogleOAuth: props.showGoogleOAuth ?? googleOAuthEnabled,
|
|
23
|
+
style: props.style ?? style,
|
|
24
|
+
allowSignup: props.allowSignup ?? allowSignup
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -23,7 +23,7 @@ function LoginHeroContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth =
|
|
|
23
23
|
poweredBy: poweredBy,
|
|
24
24
|
cardClassName: cardClassName,
|
|
25
25
|
backgroundClass: backgroundClass,
|
|
26
|
-
footer: /*#__PURE__*/ _jsxs("p", {
|
|
26
|
+
footer: signupUrl ? /*#__PURE__*/ _jsxs("p", {
|
|
27
27
|
className: "text-center text-gray-600 text-sm",
|
|
28
28
|
children: [
|
|
29
29
|
"Don't have an account?",
|
|
@@ -34,7 +34,7 @@ function LoginHeroContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth =
|
|
|
34
34
|
children: "Sign up"
|
|
35
35
|
})
|
|
36
36
|
]
|
|
37
|
-
}),
|
|
37
|
+
}) : undefined,
|
|
38
38
|
children: /*#__PURE__*/ _jsxs(AnimatePresence, {
|
|
39
39
|
mode: "wait",
|
|
40
40
|
children: [
|