@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 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, configure your project name and logo, done.
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
- style: 'hero-ui', // 'tailwind' (default) | 'hero-ui'
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
- contactEmail: 'support@myapp.com',
63
- style: 'tailwind', // or 'hero-ui'
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
- ### 3. Wire up the login action
105
+ The logo is already handled — no need to pass it. The plugin reads it from the global config.
92
106
 
93
- The plugin needs a `login` function. Pass yours from Payload's `useAuth()`:
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
- Each page component also accepts these props:
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={...} // React node — your brand logo
139
- onPasswordLogin={login} // Required — Payload's login function
140
- redirectTo="/dashboard" // Where to go after login
141
- showGoogleOAuth={true} // Show "Continue with Google" button
142
- signupUrl="/signup" // Link to signup page
143
- poweredBy={{ // Powered by logo config
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-logo.png', // Override the default Main12 logo
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
- ## Using Hooks & Services Directly
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
- Don't want the pre-built pages? Use the hooks and services to build your own:
168
+ ### Custom login with your own components
158
169
 
159
170
  ```tsx
160
- import { useLoginFlow, useVerifyOtpFlow, checkEmail, sendOtp } from '@main12/auth-login/client'
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 { email, handleEmailSubmit, ... } = useLoginFlow({
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
- // Build your own UI with these values
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
- ### Exported Hooks
215
+ ### Custom email templates
172
216
 
173
- | Hook | Purpose |
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
- ### Exported Services
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
- | Function | Description |
183
- |----------|-------------|
184
- | `checkEmail(email)` | Check if user exists and has password |
185
- | `sendOtp(email, purpose)` | Send OTP to email |
186
- | `verifyOtp(email, otp)` | Verify OTP code |
187
- | `setUserPassword(password, confirm)` | Set/update password |
188
- | `signup(name, email)` | Create new user |
189
- | `initiateGoogleLogin(redirect)` | Redirect to Google OAuth |
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
- ## Email Templates
279
+ ## ShadCN Compatibility
208
280
 
209
- Four HTML email templates in EN/ES, using the host project's Payload email adapter:
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
- - **Welcome** — sent on signup
212
- - **OTP** — 6-digit code for login or password reset
213
- - **Password Reset** — OTP email for forgot password flow
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
- Override translations or template functions via the `emails` option (Phase 2).
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
- Visits:
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
 
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  export interface AuthLayoutConfig {
3
- logo: React.ReactNode;
3
+ /** Logo as a React node. Falls back to pluginConfig.logoUrl if not provided. */
4
+ logo?: React.ReactNode;
4
5
  title?: string;
5
6
  subtitle?: string;
6
7
  poweredBy?: {
@@ -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
- (logo || title) && /*#__PURE__*/ _jsxs("div", {
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
- logo && /*#__PURE__*/ _jsx("div", {
32
+ displayLogo && /*#__PURE__*/ _jsx("div", {
21
33
  className: "flex justify-center mb-2",
22
- children: logo
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
@@ -5,4 +5,5 @@
5
5
  export type AuthStyle = 'tailwind' | 'hero-ui';
6
6
  export declare const pluginConfig: {
7
7
  style: AuthStyle;
8
+ logoUrl?: string;
8
9
  };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@main12/auth-login",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",