@main12/auth-login 0.1.3 → 0.1.4

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.
Files changed (2) hide show
  1. package/README.md +158 -103
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ pnpm add @heroui/react framer-motion @iconify/react
51
51
 
52
52
  ## Quick Start
53
53
 
54
- ### 1. Add the plugin + Users collection to your Payload config
54
+ ### 1. Add the plugin + Users collection
55
55
 
56
56
  ```ts
57
57
  import { authLoginPlugin } from '@main12/auth-login'
@@ -63,7 +63,6 @@ export default buildConfig({
63
63
  auth: { tokenExpiration: 7200, verify: false, maxLoginAttempts: 5 },
64
64
  fields: [
65
65
  { name: 'name', type: 'text' },
66
- // OTP fields required by the plugin:
67
66
  { name: 'otpHash', type: 'text', admin: { hidden: true } },
68
67
  { name: 'otpAttempts', type: 'number', admin: { hidden: true } },
69
68
  { name: 'otpExpiresAt', type: 'text', admin: { hidden: true } },
@@ -74,40 +73,36 @@ export default buildConfig({
74
73
  authLoginPlugin({
75
74
  projectName: 'My App',
76
75
  domain: 'https://myapp.com',
77
- logo: '/logo.png', // shown automatically on all auth pages
78
- style: 'hero-ui', // or 'tailwind'
76
+ logo: '/logo.png',
77
+ style: 'hero-ui',
79
78
  }),
80
79
  ],
81
80
  })
82
81
  ```
83
82
 
84
- ### 2. Add auth pages to your app
85
-
86
- Create one-line route files under `src/app/(frontend)/(auth)/`:
83
+ ### 2. Create one-line route files
87
84
 
88
85
  ```tsx
89
- // login/page.tsx
86
+ // src/app/(frontend)/(auth)/login/page.tsx
90
87
  export { LoginPage as default } from '@main12/auth-login/client'
91
88
 
92
- // signup/page.tsx
89
+ // src/app/(frontend)/(auth)/signup/page.tsx
93
90
  export { SignupPage as default } from '@main12/auth-login/client'
94
91
 
95
- // forgot-password/page.tsx
92
+ // src/app/(frontend)/(auth)/forgot-password/page.tsx
96
93
  export { ForgotPasswordPage as default } from '@main12/auth-login/client'
97
94
 
98
- // verify-otp/page.tsx
95
+ // src/app/(frontend)/(auth)/verify-otp/page.tsx
99
96
  export { VerifyOtpPage as default } from '@main12/auth-login/client'
100
97
 
101
- // set-password/page.tsx
98
+ // src/app/(frontend)/(auth)/set-password/page.tsx
102
99
  export { SetPasswordPage as default } from '@main12/auth-login/client'
103
100
  ```
104
101
 
105
- The logo is already handled — no need to pass it. The plugin reads it from the global config.
106
-
107
102
  ### 3. Wire up the login action
108
103
 
109
104
  ```tsx
110
- // login/page.tsx — override with your Payload login function:
105
+ // login/page.tsx
111
106
  'use client'
112
107
  import { LoginPage } from '@main12/auth-login/client'
113
108
  import { useAuth } from '@/providers/Auth'
@@ -118,93 +113,121 @@ export default function Page() {
118
113
  }
119
114
  ```
120
115
 
121
- ### 4. That's it — visit `/login`
116
+ ### 4. Visit `/login` — done.
122
117
 
123
118
  ---
124
119
 
125
- ## Configuration Options
120
+ ## Configuration
126
121
 
127
122
  ```ts
128
123
  authLoginPlugin({
129
- // === Branding ===
130
- projectName: 'My App', // Used in email subjects and footers
124
+ projectName: 'My App', // Email subjects + footers
131
125
  contactEmail: 'hi@myapp.com', // Email footer contact
132
126
  domain: 'https://myapp.com', // Links in emails
133
- logo: '/logo.png', // Shown on all auth pages + email headers
134
-
135
- // === Style ===
136
- style: 'tailwind', // 'tailwind' (default) | 'hero-ui'
137
-
138
- // === Enable/Disable ===
139
- enabled: true, // Set false to disable the plugin
127
+ logo: '/logo.png', // All auth pages + email headers
128
+ style: 'tailwind', // 'tailwind' | 'hero-ui'
129
+ enabled: true,
140
130
  })
141
131
  ```
142
132
 
143
133
  ### Per-page overrides
144
134
 
145
- Each page component also accepts these props for project-specific customization:
146
-
147
- ```ts
135
+ ```tsx
148
136
  <LoginPage
149
137
  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
155
- enabled: true,
156
- logoUrl: '/custom.png', // Override default Main12 logo
157
- linkUrl: 'https://your-site.com',
158
- }}
138
+ onPasswordLogin={login} // Required
139
+ redirectTo="/dashboard"
140
+ showGoogleOAuth={true}
141
+ signupUrl="/signup"
142
+ poweredBy={{ enabled: true, logoUrl: '/custom.png', linkUrl: 'https://...' }}
159
143
  />
160
144
  ```
161
145
 
162
146
  ---
163
147
 
164
- ## Building Custom Auth Pages
148
+ ## Building Custom Pages
149
+
150
+ Use the plugin's hooks to build your own UI with any component library (HeroUI, shadcn, plain Tailwind).
151
+
152
+ ### Hook Quick-Reference
165
153
 
166
- You can build your own UI while reusing the plugin's hooks, services, and endpoints.
154
+ | Hook | Returns | Key inputs |
155
+ |------|---------|------------|
156
+ | `useLoginFlow({ redirectTo, onPasswordLogin })` | `step, email, password, error, isLoading, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail` | `redirectTo: string`, `onPasswordLogin: (creds) => Promise<void>` |
157
+ | `useSignupFlow()` | `name, email, error, isLoading, setName, setEmail, handleSubmit` | none — manages its own state |
158
+ | `useForgotPasswordFlow()` | `email, error, isLoading, setEmail, handleSubmit` | none |
159
+ | `useVerifyOtpFlow({ email, purpose, redirectTo })` | `otp, error, isLoading, isResending, resendCooldown, setOtp, handleSubmit, handleResendCode` | `email: string`, `purpose: 'login'\|'signup'\|'password-reset'` |
160
+ | `useSetPasswordFlow({ redirectTo })` | `password, confirmPassword, error, isLoading, strength, setPassword, setConfirmPassword, handleSubmit` | `redirectTo: string` |
161
+ | `useAuth()` (from Payload) | `user, login, logout, create` | (your existing Payload auth provider) |
167
162
 
168
- ### Custom login with your own components
163
+ ### Full Example: Custom Login Page
169
164
 
170
165
  ```tsx
171
166
  'use client'
172
167
  import { useLoginFlow } from '@main12/auth-login/client'
173
- import { Button, Input } from '@heroui/react' // or shadcn, or plain HTML
174
168
  import { useAuth } from '@/providers/Auth'
175
169
 
176
- export default function MyCustomLogin() {
170
+ export default function CustomLogin() {
177
171
  const { login } = useAuth()
178
172
  const {
179
173
  step, email, password, error, isLoading, showPassword,
180
174
  setEmail, setPassword, setShowPassword,
181
175
  handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail,
182
- } = useLoginFlow({
183
- redirectTo: '/dashboard',
184
- onPasswordLogin: login,
185
- })
176
+ } = useLoginFlow({ redirectTo: '/dashboard', onPasswordLogin: login })
186
177
 
187
178
  return (
188
- <div className="min-h-screen flex items-center justify-center">
179
+ <div className="min-h-screen flex items-center justify-center bg-gray-50">
189
180
  <div className="w-full max-w-md p-8 bg-white rounded-2xl shadow-xl">
181
+ <img src="/logo.png" className="mx-auto mb-6" width={180} alt="" />
182
+
183
+ {/* Step 1: Email */}
190
184
  {step === 'email' && (
191
- <form onSubmit={handleEmailSubmit}>
192
- <Input type="email" label="Email" value={email} onValueChange={setEmail} />
193
- <Button type="submit" isLoading={isLoading}>Continue</Button>
185
+ <form onSubmit={handleEmailSubmit} className="space-y-4">
186
+ <h1 className="text-xl font-semibold text-center">Welcome Back</h1>
187
+ {error && <p className="text-red-600 text-sm">{error}</p>}
188
+ <input type="email" value={email} onChange={e => setEmail(e.target.value)}
189
+ placeholder="Email" required className="w-full h-12 px-4 border rounded-xl" />
190
+ <button type="submit" disabled={isLoading}
191
+ className="w-full h-12 bg-[#D5E855] rounded-full font-semibold">
192
+ {isLoading ? 'Loading...' : 'Continue'}
193
+ </button>
194
194
  </form>
195
195
  )}
196
+
197
+ {/* Step 2a: Password */}
196
198
  {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>
199
+ <form onSubmit={handlePasswordSubmit} className="space-y-4">
200
+ <div className="flex justify-between border rounded-xl px-4 py-3">
201
+ <span>{email}</span>
202
+ <button type="button" onClick={handleEditEmail} className="text-sm">Edit</button>
203
+ </div>
204
+ {error && <p className="text-red-600 text-sm">{error}</p>}
205
+ <input type={showPassword ? 'text' : 'password'} value={password}
206
+ onChange={e => setPassword(e.target.value)} placeholder="Password" required
207
+ className="w-full h-12 px-4 border rounded-xl" autoFocus />
208
+ <button type="submit" disabled={isLoading}
209
+ className="w-full h-12 bg-[#D5E855] rounded-full font-semibold">
210
+ {isLoading ? 'Loading...' : 'Sign In'}
211
+ </button>
201
212
  </form>
202
213
  )}
214
+
215
+ {/* Step 2b: OTP Prompt (migrated users without password) */}
203
216
  {step === 'otp-prompt' && (
204
- <>
205
- <p>{email} <button onClick={handleEditEmail}>Edit</button></p>
206
- <Button onPress={handleSendOtp} isLoading={isLoading}>Send Code</Button>
207
- </>
217
+ <div className="space-y-4">
218
+ <div className="flex justify-between border rounded-xl px-4 py-3">
219
+ <span>{email}</span>
220
+ <button type="button" onClick={handleEditEmail}>Edit</button>
221
+ </div>
222
+ {error && <p className="text-red-600 text-sm">{error}</p>}
223
+ <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-sm text-blue-700">
224
+ We'll send a verification code to this email.
225
+ </div>
226
+ <button onClick={handleSendOtp} disabled={isLoading}
227
+ className="w-full h-12 bg-[#D5E855] rounded-full font-semibold">
228
+ {isLoading ? 'Sending...' : 'Send Code'}
229
+ </button>
230
+ </div>
208
231
  )}
209
232
  </div>
210
233
  </div>
@@ -212,60 +235,50 @@ export default function MyCustomLogin() {
212
235
  }
213
236
  ```
214
237
 
215
- ### Custom email templates
238
+ ### Custom Signup
216
239
 
217
- Override individual email translations or entire template functions:
240
+ Same pattern — call `onSignup(name, email)` from your form, redirect to `/verify-otp?email=...&purpose=signup` on success.
218
241
 
219
- ```ts
220
- import { getEmailTranslations } from '@main12/auth-login/rsc'
242
+ ### Custom Verify OTP
243
+
244
+ Use `useVerifyOtpFlow({ email, purpose, redirectTo })`. It manages the 6-digit input, verification, resend cooldown (60s), and auto-redirect. All you need is an `<input>` or `<InputOtp>` for the code and a verify button.
245
+
246
+ ### Custom Forgot Password
247
+
248
+ Use `useForgotPasswordFlow()`. Collect email, call `handleSubmit(e)`. It checks user exists, sends OTP, and redirects to `/verify-otp?email=...&purpose=password-reset`.
249
+
250
+ ### Custom Set Password
251
+
252
+ Use `useSetPasswordFlow({ redirectTo })`. Two password fields (new + confirm). The hook validates strength (≥8 chars, 3/4 criteria) and matches. Returns `strength.score` (0-5) for a visual indicator.
253
+
254
+ ---
221
255
 
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:'
256
+ ## Custom Email Templates
226
257
 
227
- // Option 2: Import template generators and wrap them
228
- import { generateWelcomeEmail, generateOtpEmail } from '@main12/auth-login/rsc'
258
+ ```ts
259
+ import { generateWelcomeEmail, getEmailTranslations } from '@main12/auth-login/rsc'
260
+
261
+ // Override translations
262
+ const t = getEmailTranslations('en')
263
+ t.welcome.subject = 'Welcome to My SaaS! 🚀'
229
264
 
230
- function myWelcomeEmail(params) {
265
+ // Or wrap template generators
266
+ function myWelcome(params) {
231
267
  const base = generateWelcomeEmail(params)
232
- return {
233
- ...base,
234
- html: base.html.replace('Get Started', 'Launch Dashboard'),
235
- }
268
+ return { ...base, html: base.html.replace('Get Started', 'Launch Now') }
236
269
  }
237
270
 
238
- // Use in your Payload hooks or custom endpoints
271
+ // Use in hooks/endpoints
239
272
  await payload.sendEmail({
240
273
  to: user.email,
241
- subject: myWelcomeEmail({ userName: user.name }).subject,
242
- html: myWelcomeEmail({ userName: user.name }).html,
274
+ ...myWelcome({ userName: user.name, userEmail: user.email }),
243
275
  })
244
276
  ```
245
277
 
246
278
  ---
247
279
 
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 |
262
-
263
- ---
264
-
265
280
  ## API Endpoints
266
281
 
267
- Registered automatically by the plugin:
268
-
269
282
  | Method | Path | Description |
270
283
  |--------|------|-------------|
271
284
  | POST | `/api/auth/check-email` | Check if email is registered |
@@ -278,20 +291,62 @@ Registered automatically by the plugin:
278
291
 
279
292
  ## ShadCN Compatibility
280
293
 
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:
294
+ 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
+
296
+ ---
297
+
298
+ ## 🤖 AI Agent Prompts
299
+
300
+ Copy these prompts into Claude, Cursor, Copilot, or any AI agent.
301
+
302
+ ### Prompt: Set up the auth plugin in a new Payload project
282
303
 
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
304
+ ```
305
+ Add @main12/auth-login to this Payload project:
306
+
307
+ 1. Install: pnpm add @main12/auth-login
308
+ 2. In payload.config.ts, add:
309
+ - Users collection with auth enabled + otpHash, otpAttempts, otpExpiresAt fields
310
+ - Plugin: authLoginPlugin({ projectName: "<PROJECT>", domain: "<URL>", logo: "/logo.png", style: "hero-ui" })
311
+ 3. Create route files under src/app/(frontend)/(auth)/:
312
+ - login/page.tsx → export { LoginPage as default } from '@main12/auth-login/client'
313
+ - signup/page.tsx → export { SignupPage as default } from '@main12/auth-login/client'
314
+ - forgot-password/page.tsx → export { ForgotPasswordPage as default } from '@main12/auth-login/client'
315
+ - verify-otp/page.tsx → export { VerifyOtpPage as default } from '@main12/auth-login/client'
316
+ - set-password/page.tsx → export { SetPasswordPage as default } from '@main12/auth-login/client'
317
+ 4. In login/page.tsx, wrap LoginPage with useAuth() to pass onPasswordLogin={login}.
318
+ 5. Verify: visit /login
319
+ ```
320
+
321
+ ### Prompt: Build a custom login page with HeroUI components
322
+
323
+ ```
324
+ Build a custom login page using @main12/auth-login hooks and @heroui/react:
325
+
326
+ - Import useLoginFlow from '@main12/auth-login/client'
327
+ - Use useAuth() from Payload's Auth provider for the login function
328
+ - Render 3 steps: email input → password input / OTP prompt
329
+ - Use HeroUI <Button>, <Input> with variant="bordered", rounded-full
330
+ - Add "Continue with Google" button using initiateGoogleLogin from the plugin
331
+ - Add "Powered by Main12" footer from the plugin's PoweredBy component
332
+ ```
286
333
 
287
- No need for a separate `style: 'shadcn'` — the Tailwind style already renders compatible markup, and custom pages give you full ShadCN component control.
334
+ ### Prompt: Customize email templates for my project
335
+
336
+ ```
337
+ Customize the email templates from @main12/auth-login for my project "<PROJECT_NAME>":
338
+
339
+ - Import generateWelcomeEmail, generateOtpEmail, generatePasswordResetEmail, generatePasswordChangedEmail from '@main12/auth-login/rsc'
340
+ - Override each to use my brand colors (primary: <COLOR>, accent: <COLOR>)
341
+ - Change the welcome email CTA text to "<CUSTOM_TEXT>"
342
+ - Change the OTP email purpose text to "<CUSTOM_TEXT>"
343
+ - Add my project's social media links to the footer
344
+ ```
288
345
 
289
346
  ---
290
347
 
291
348
  ## Dev Testing
292
349
 
293
- This repo ships with a dev harness. To test locally:
294
-
295
350
  ```bash
296
351
  git clone https://github.com/MAIN-12/auth-login-plugin.git
297
352
  cd auth-login-plugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@main12/auth-login",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Reusable Payload CMS auth plugin — login, signup, OTP, forgot password, branded emails, Powered by Main12",
5
5
  "license": "MIT",
6
6
  "type": "module",