@main12/auth-login 0.1.2 → 0.1.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 +137 -71
- package/dist/components/AuthLayout.d.ts +2 -1
- package/dist/components/AuthLayout.js +15 -3
- package/dist/config.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @main12/auth-login
|
|
2
2
|
|
|
3
|
-
**Payload CMS authentication plugin** — login, signup, OTP, forgot password, branded emails, and "Powered by Main 12" footer. Install once,
|
|
3
|
+
**Payload CMS authentication plugin** — login, signup, OTP, forgot password, branded emails, and "Powered by Main 12" footer. Install once, done.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
// payload.config.ts
|
|
@@ -10,7 +10,8 @@ plugins: [
|
|
|
10
10
|
authLoginPlugin({
|
|
11
11
|
projectName: 'My SaaS',
|
|
12
12
|
domain: 'https://myapp.com',
|
|
13
|
-
|
|
13
|
+
logo: 'https://myapp.com/logo.png', // shown in all auth pages + emails
|
|
14
|
+
style: 'hero-ui', // 'tailwind' (default) | 'hero-ui'
|
|
14
15
|
}),
|
|
15
16
|
]
|
|
16
17
|
```
|
|
@@ -24,7 +25,8 @@ plugins: [
|
|
|
24
25
|
- **5 API endpoints** — `check-email`, `otp/send`, `otp/verify`, `set-password`, `signup`
|
|
25
26
|
- **OTP engine** — SHA-256 hashing + `timingSafeEqual` comparison, 10-min expiry, 3 attempts
|
|
26
27
|
- **Email templates** — welcome, OTP login, password reset, password changed (EN/ES)
|
|
27
|
-
- **Powered by Main 12** — bundled inline SVG, linked to main12.com by default
|
|
28
|
+
- **Powered by Main 12** — bundled inline SVG, linked to main12.com by default (URL overridable)
|
|
29
|
+
- **Global logo** — pass once in plugin config, automatically shown on all pages and email headers
|
|
28
30
|
- **Zero runtime deps** (Tailwind mode) — Next.js + React are peer dependencies
|
|
29
31
|
|
|
30
32
|
---
|
|
@@ -35,9 +37,9 @@ plugins: [
|
|
|
35
37
|
pnpm add @main12/auth-login
|
|
36
38
|
```
|
|
37
39
|
|
|
38
|
-
### Tailwind mode (default, zero UI deps)
|
|
40
|
+
### Tailwind mode (default, zero UI deps — also ShadCN compatible)
|
|
39
41
|
|
|
40
|
-
No extra dependencies needed.
|
|
42
|
+
No extra dependencies needed. The Tailwind style uses standard utility classes that work in any Tailwind project, including ShadCN-based ones.
|
|
41
43
|
|
|
42
44
|
### HeroUI mode
|
|
43
45
|
|
|
@@ -49,21 +51,33 @@ pnpm add @heroui/react framer-motion @iconify/react
|
|
|
49
51
|
|
|
50
52
|
## Quick Start
|
|
51
53
|
|
|
52
|
-
### 1. Add the plugin to your Payload config
|
|
54
|
+
### 1. Add the plugin + Users collection to your Payload config
|
|
53
55
|
|
|
54
56
|
```ts
|
|
55
57
|
import { authLoginPlugin } from '@main12/auth-login'
|
|
56
58
|
|
|
57
59
|
export default buildConfig({
|
|
60
|
+
collections: [
|
|
61
|
+
{
|
|
62
|
+
slug: 'users',
|
|
63
|
+
auth: { tokenExpiration: 7200, verify: false, maxLoginAttempts: 5 },
|
|
64
|
+
fields: [
|
|
65
|
+
{ name: 'name', type: 'text' },
|
|
66
|
+
// OTP fields required by the plugin:
|
|
67
|
+
{ name: 'otpHash', type: 'text', admin: { hidden: true } },
|
|
68
|
+
{ name: 'otpAttempts', type: 'number', admin: { hidden: true } },
|
|
69
|
+
{ name: 'otpExpiresAt', type: 'text', admin: { hidden: true } },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
58
73
|
plugins: [
|
|
59
74
|
authLoginPlugin({
|
|
60
75
|
projectName: 'My App',
|
|
61
76
|
domain: 'https://myapp.com',
|
|
62
|
-
|
|
63
|
-
style: '
|
|
77
|
+
logo: '/logo.png', // shown automatically on all auth pages
|
|
78
|
+
style: 'hero-ui', // or 'tailwind'
|
|
64
79
|
}),
|
|
65
80
|
],
|
|
66
|
-
// ... rest of your config
|
|
67
81
|
})
|
|
68
82
|
```
|
|
69
83
|
|
|
@@ -88,30 +102,24 @@ export { VerifyOtpPage as default } from '@main12/auth-login/client'
|
|
|
88
102
|
export { SetPasswordPage as default } from '@main12/auth-login/client'
|
|
89
103
|
```
|
|
90
104
|
|
|
91
|
-
|
|
105
|
+
The logo is already handled — no need to pass it. The plugin reads it from the global config.
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
### 3. Wire up the login action
|
|
94
108
|
|
|
95
109
|
```tsx
|
|
96
|
-
// login/page.tsx
|
|
110
|
+
// login/page.tsx — override with your Payload login function:
|
|
97
111
|
'use client'
|
|
98
112
|
import { LoginPage } from '@main12/auth-login/client'
|
|
99
113
|
import { useAuth } from '@/providers/Auth'
|
|
100
|
-
import AppLogo from '@/components/Logo/AppLogo'
|
|
101
114
|
|
|
102
115
|
export default function Page() {
|
|
103
116
|
const { login } = useAuth()
|
|
104
|
-
return
|
|
105
|
-
<LoginPage
|
|
106
|
-
logo={<AppLogo width={180} height={42} />}
|
|
107
|
-
onPasswordLogin={login}
|
|
108
|
-
redirectTo="/dashboard"
|
|
109
|
-
poweredBy={{ enabled: true }}
|
|
110
|
-
/>
|
|
111
|
-
)
|
|
117
|
+
return <LoginPage onPasswordLogin={login} redirectTo="/dashboard" />
|
|
112
118
|
}
|
|
113
119
|
```
|
|
114
120
|
|
|
121
|
+
### 4. That's it — visit `/login`
|
|
122
|
+
|
|
115
123
|
---
|
|
116
124
|
|
|
117
125
|
## Configuration Options
|
|
@@ -122,71 +130,135 @@ authLoginPlugin({
|
|
|
122
130
|
projectName: 'My App', // Used in email subjects and footers
|
|
123
131
|
contactEmail: 'hi@myapp.com', // Email footer contact
|
|
124
132
|
domain: 'https://myapp.com', // Links in emails
|
|
133
|
+
logo: '/logo.png', // Shown on all auth pages + email headers
|
|
125
134
|
|
|
126
135
|
// === Style ===
|
|
127
|
-
style: 'tailwind', // 'tailwind' | 'hero-ui'
|
|
136
|
+
style: 'tailwind', // 'tailwind' (default) | 'hero-ui'
|
|
128
137
|
|
|
129
138
|
// === Enable/Disable ===
|
|
130
139
|
enabled: true, // Set false to disable the plugin
|
|
131
140
|
})
|
|
132
141
|
```
|
|
133
142
|
|
|
134
|
-
|
|
143
|
+
### Per-page overrides
|
|
144
|
+
|
|
145
|
+
Each page component also accepts these props for project-specific customization:
|
|
135
146
|
|
|
136
147
|
```ts
|
|
137
148
|
<LoginPage
|
|
138
|
-
logo={
|
|
139
|
-
onPasswordLogin={login}
|
|
140
|
-
redirectTo="/dashboard"
|
|
141
|
-
showGoogleOAuth={true}
|
|
142
|
-
signupUrl="/signup"
|
|
143
|
-
poweredBy={{
|
|
149
|
+
logo={<AppLogo width={180} />} // Override global logo (optional)
|
|
150
|
+
onPasswordLogin={login} // Required — Payload's login function
|
|
151
|
+
redirectTo="/dashboard" // Where to go after login
|
|
152
|
+
showGoogleOAuth={true} // Show "Continue with Google" button
|
|
153
|
+
signupUrl="/signup" // Link to signup page
|
|
154
|
+
poweredBy={{ // Powered by logo config
|
|
144
155
|
enabled: true,
|
|
145
|
-
logoUrl: '/custom
|
|
156
|
+
logoUrl: '/custom.png', // Override default Main12 logo
|
|
146
157
|
linkUrl: 'https://your-site.com',
|
|
147
|
-
width: 28,
|
|
148
|
-
height: 28,
|
|
149
158
|
}}
|
|
150
159
|
/>
|
|
151
160
|
```
|
|
152
161
|
|
|
153
162
|
---
|
|
154
163
|
|
|
155
|
-
##
|
|
164
|
+
## Building Custom Auth Pages
|
|
165
|
+
|
|
166
|
+
You can build your own UI while reusing the plugin's hooks, services, and endpoints.
|
|
156
167
|
|
|
157
|
-
|
|
168
|
+
### Custom login with your own components
|
|
158
169
|
|
|
159
170
|
```tsx
|
|
160
|
-
|
|
171
|
+
'use client'
|
|
172
|
+
import { useLoginFlow } from '@main12/auth-login/client'
|
|
173
|
+
import { Button, Input } from '@heroui/react' // or shadcn, or plain HTML
|
|
174
|
+
import { useAuth } from '@/providers/Auth'
|
|
161
175
|
|
|
162
|
-
function MyCustomLogin() {
|
|
163
|
-
const {
|
|
176
|
+
export default function MyCustomLogin() {
|
|
177
|
+
const { login } = useAuth()
|
|
178
|
+
const {
|
|
179
|
+
step, email, password, error, isLoading, showPassword,
|
|
180
|
+
setEmail, setPassword, setShowPassword,
|
|
181
|
+
handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail,
|
|
182
|
+
} = useLoginFlow({
|
|
164
183
|
redirectTo: '/dashboard',
|
|
165
184
|
onPasswordLogin: login,
|
|
166
185
|
})
|
|
167
|
-
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
189
|
+
<div className="w-full max-w-md p-8 bg-white rounded-2xl shadow-xl">
|
|
190
|
+
{step === 'email' && (
|
|
191
|
+
<form onSubmit={handleEmailSubmit}>
|
|
192
|
+
<Input type="email" label="Email" value={email} onValueChange={setEmail} />
|
|
193
|
+
<Button type="submit" isLoading={isLoading}>Continue</Button>
|
|
194
|
+
</form>
|
|
195
|
+
)}
|
|
196
|
+
{step === 'password' && (
|
|
197
|
+
<form onSubmit={handlePasswordSubmit}>
|
|
198
|
+
<p>Signing in as {email} <button onClick={handleEditEmail}>Edit</button></p>
|
|
199
|
+
<Input type="password" label="Password" value={password} onValueChange={setPassword} />
|
|
200
|
+
<Button type="submit" isLoading={isLoading}>Sign In</Button>
|
|
201
|
+
</form>
|
|
202
|
+
)}
|
|
203
|
+
{step === 'otp-prompt' && (
|
|
204
|
+
<>
|
|
205
|
+
<p>{email} <button onClick={handleEditEmail}>Edit</button></p>
|
|
206
|
+
<Button onPress={handleSendOtp} isLoading={isLoading}>Send Code</Button>
|
|
207
|
+
</>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
)
|
|
168
212
|
}
|
|
169
213
|
```
|
|
170
214
|
|
|
171
|
-
###
|
|
215
|
+
### Custom email templates
|
|
172
216
|
|
|
173
|
-
|
|
174
|
-
|------|---------|
|
|
175
|
-
| `useLoginFlow` | Multi-step login state machine (email → password/OTP) |
|
|
176
|
-
| `useVerifyOtpFlow` | OTP input, verify, resend with cooldown |
|
|
177
|
-
| `useForgotPasswordFlow` | Email → check → send OTP |
|
|
178
|
-
| `useSetPasswordFlow` | Set password with strength indicator |
|
|
217
|
+
Override individual email translations or entire template functions:
|
|
179
218
|
|
|
180
|
-
|
|
219
|
+
```ts
|
|
220
|
+
import { getEmailTranslations } from '@main12/auth-login/rsc'
|
|
221
|
+
|
|
222
|
+
// Option 1: Deep-merge translations
|
|
223
|
+
const myTranslations = getEmailTranslations('en')
|
|
224
|
+
myTranslations.welcome.subject = 'Welcome to My SaaS! 🚀'
|
|
225
|
+
myTranslations.otp.purposeLogin = 'Use this code to access your dashboard:'
|
|
226
|
+
|
|
227
|
+
// Option 2: Import template generators and wrap them
|
|
228
|
+
import { generateWelcomeEmail, generateOtpEmail } from '@main12/auth-login/rsc'
|
|
229
|
+
|
|
230
|
+
function myWelcomeEmail(params) {
|
|
231
|
+
const base = generateWelcomeEmail(params)
|
|
232
|
+
return {
|
|
233
|
+
...base,
|
|
234
|
+
html: base.html.replace('Get Started', 'Launch Dashboard'),
|
|
235
|
+
}
|
|
236
|
+
}
|
|
181
237
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
238
|
+
// Use in your Payload hooks or custom endpoints
|
|
239
|
+
await payload.sendEmail({
|
|
240
|
+
to: user.email,
|
|
241
|
+
subject: myWelcomeEmail({ userName: user.name }).subject,
|
|
242
|
+
html: myWelcomeEmail({ userName: user.name }).html,
|
|
243
|
+
})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Exported Hooks & Services
|
|
249
|
+
|
|
250
|
+
| Hook / Service | Type | Purpose |
|
|
251
|
+
|---------------|------|---------|
|
|
252
|
+
| `useLoginFlow` | Hook | Multi-step login state machine (email → password/OTP) |
|
|
253
|
+
| `useVerifyOtpFlow` | Hook | OTP input, verify, resend with cooldown |
|
|
254
|
+
| `useForgotPasswordFlow` | Hook | Email → check → send OTP |
|
|
255
|
+
| `useSetPasswordFlow` | Hook | Set password with strength indicator |
|
|
256
|
+
| `checkEmail(email)` | Service | Check if user exists and has password |
|
|
257
|
+
| `sendOtp(email, purpose)` | Service | Send OTP to email |
|
|
258
|
+
| `verifyOtp(email, otp)` | Service | Verify OTP code |
|
|
259
|
+
| `setUserPassword(pw, confirm)` | Service | Set/update password |
|
|
260
|
+
| `signup(name, email)` | Service | Create new user |
|
|
261
|
+
| `initiateGoogleLogin(redirect)` | Service | Redirect to Google OAuth |
|
|
190
262
|
|
|
191
263
|
---
|
|
192
264
|
|
|
@@ -197,23 +269,22 @@ Registered automatically by the plugin:
|
|
|
197
269
|
| Method | Path | Description |
|
|
198
270
|
|--------|------|-------------|
|
|
199
271
|
| POST | `/api/auth/check-email` | Check if email is registered |
|
|
200
|
-
| POST | `/api/auth/otp/send` | Generate + send OTP |
|
|
201
|
-
| POST | `/api/auth/otp/verify` | Verify OTP + login |
|
|
272
|
+
| POST | `/api/auth/otp/send` | Generate + send OTP via Payload email adapter |
|
|
273
|
+
| POST | `/api/auth/otp/verify` | Verify OTP + login (sets httpOnly cookie) |
|
|
202
274
|
| POST | `/api/auth/set-password` | Set/update password |
|
|
203
|
-
| POST | `/api/auth/signup` | Create account |
|
|
275
|
+
| POST | `/api/auth/signup` | Create account + send welcome email |
|
|
204
276
|
|
|
205
277
|
---
|
|
206
278
|
|
|
207
|
-
##
|
|
279
|
+
## ShadCN Compatibility
|
|
208
280
|
|
|
209
|
-
|
|
281
|
+
The `tailwind` style uses standard Tailwind utility classes — it works in ShadCN projects out of the box. If you want ShadCN components instead of plain HTML:
|
|
210
282
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- **Password Changed** — confirmation after password update
|
|
283
|
+
1. Follow the [Custom Auth Pages](#building-custom-auth-pages) guide above
|
|
284
|
+
2. Import `useLoginFlow`, `useVerifyOtpFlow`, etc. from the plugin
|
|
285
|
+
3. Use your ShadCN `<Button>`, `<Input>`, `<Card>` components with the same hook values
|
|
215
286
|
|
|
216
|
-
|
|
287
|
+
No need for a separate `style: 'shadcn'` — the Tailwind style already renders compatible markup, and custom pages give you full ShadCN component control.
|
|
217
288
|
|
|
218
289
|
---
|
|
219
290
|
|
|
@@ -222,18 +293,13 @@ Override translations or template functions via the `emails` option (Phase 2).
|
|
|
222
293
|
This repo ships with a dev harness. To test locally:
|
|
223
294
|
|
|
224
295
|
```bash
|
|
296
|
+
git clone https://github.com/MAIN-12/auth-login-plugin.git
|
|
225
297
|
cd auth-login-plugin
|
|
226
298
|
pnpm install
|
|
227
299
|
pnpm dev
|
|
228
300
|
```
|
|
229
301
|
|
|
230
|
-
|
|
231
|
-
- `http://localhost:3000/login` — multi-step login
|
|
232
|
-
- `http://localhost:3000/signup` — create account
|
|
233
|
-
- `http://localhost:3000/forgot-password` — password reset
|
|
234
|
-
- `http://localhost:3000/admin` — Payload admin panel
|
|
235
|
-
|
|
236
|
-
The dev config uses SQLite (no external DB needed) with a Users collection pre-configured.
|
|
302
|
+
Visit `http://localhost:3000/login` — all 5 auth pages wired with SQLite.
|
|
237
303
|
|
|
238
304
|
---
|
|
239
305
|
|
|
@@ -3,7 +3,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Card, CardContent, CardFooter } from './ui/index.js';
|
|
5
5
|
import { PoweredBy } from './PoweredBy.js';
|
|
6
|
+
import { pluginConfig } from '../config.js';
|
|
7
|
+
function DefaultLogo() {
|
|
8
|
+
if (!pluginConfig.logoUrl) return null;
|
|
9
|
+
return /*#__PURE__*/ _jsx("img", {
|
|
10
|
+
src: pluginConfig.logoUrl,
|
|
11
|
+
alt: "",
|
|
12
|
+
width: 180,
|
|
13
|
+
height: 42,
|
|
14
|
+
className: "object-contain"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
6
17
|
export const AuthLayout = ({ children, logo, title, subtitle, footer, poweredBy, cardClassName = '', backgroundClass = 'bg-white md:bg-[#191919]' })=>{
|
|
18
|
+
const displayLogo = logo || /*#__PURE__*/ _jsx(DefaultLogo, {});
|
|
7
19
|
return /*#__PURE__*/ _jsx("main", {
|
|
8
20
|
className: `flex flex-col min-h-screen ${backgroundClass}`,
|
|
9
21
|
children: /*#__PURE__*/ _jsx("div", {
|
|
@@ -14,12 +26,12 @@ export const AuthLayout = ({ children, logo, title, subtitle, footer, poweredBy,
|
|
|
14
26
|
/*#__PURE__*/ _jsxs(Card, {
|
|
15
27
|
className: cardClassName,
|
|
16
28
|
children: [
|
|
17
|
-
(
|
|
29
|
+
(displayLogo || title) && /*#__PURE__*/ _jsxs("div", {
|
|
18
30
|
className: "flex flex-col items-center gap-2 pt-6 pb-2 px-6",
|
|
19
31
|
children: [
|
|
20
|
-
|
|
32
|
+
displayLogo && /*#__PURE__*/ _jsx("div", {
|
|
21
33
|
className: "flex justify-center mb-2",
|
|
22
|
-
children:
|
|
34
|
+
children: displayLogo
|
|
23
35
|
}),
|
|
24
36
|
title && /*#__PURE__*/ _jsx("h1", {
|
|
25
37
|
className: "text-xl font-semibold text-gray-900 text-center",
|
package/dist/config.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface AuthLoginPluginOptions {
|
|
|
7
7
|
domain?: string;
|
|
8
8
|
/** UI style for auth pages: 'tailwind' (default) or 'hero-ui' */
|
|
9
9
|
style?: AuthStyle;
|
|
10
|
+
/** Logo shown in all auth pages and email headers. Can be a URL string. */
|
|
11
|
+
logo?: string;
|
|
10
12
|
}
|
|
11
13
|
export declare const authLoginPlugin: (options?: AuthLoginPluginOptions) => (config: Config) => Config;
|
|
12
14
|
export { pluginConfig };
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export const authLoginPlugin = (options = {})=>(config)=>{
|
|
|
4
4
|
if (options.enabled === false) return config;
|
|
5
5
|
// Set global style config — all page components read this at render time
|
|
6
6
|
pluginConfig.style = options.style || 'tailwind';
|
|
7
|
+
pluginConfig.logoUrl = options.logo;
|
|
7
8
|
// Register auth API endpoints
|
|
8
9
|
config.endpoints = [
|
|
9
10
|
...config.endpoints || [],
|
package/package.json
CHANGED