@main12/auth-login 0.1.6 → 0.1.8

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.
@@ -4,9 +4,13 @@ import { pluginConfig } from '../../config.js';
4
4
  import LoginPageTailwind from './LoginPageTailwind.js';
5
5
  import LoginPageHero from './LoginPageHero.js';
6
6
  export default function LoginPage(props) {
7
- return pluginConfig.style === 'hero-ui' ? /*#__PURE__*/ _jsx(LoginPageHero, {
7
+ const resolved = {
8
+ showGoogleOAuth: pluginConfig.googleOAuthEnabled,
8
9
  ...props
10
+ };
11
+ return pluginConfig.style === 'hero-ui' ? /*#__PURE__*/ _jsx(LoginPageHero, {
12
+ ...resolved
9
13
  }) : /*#__PURE__*/ _jsx(LoginPageTailwind, {
10
- ...props
14
+ ...resolved
11
15
  });
12
16
  }
@@ -4,9 +4,13 @@ import { pluginConfig } from '../../config.js';
4
4
  import SignupPageTailwind from './SignupPageTailwind.js';
5
5
  import SignupPageHero from './SignupPageHero.js';
6
6
  export default function SignupPage(props) {
7
- return pluginConfig.style === 'hero-ui' ? /*#__PURE__*/ _jsx(SignupPageHero, {
7
+ const resolved = {
8
+ showGoogleOAuth: pluginConfig.googleOAuthEnabled,
8
9
  ...props
10
+ };
11
+ return pluginConfig.style === 'hero-ui' ? /*#__PURE__*/ _jsx(SignupPageHero, {
12
+ ...resolved
9
13
  }) : /*#__PURE__*/ _jsx(SignupPageTailwind, {
10
- ...props
14
+ ...resolved
11
15
  });
12
16
  }
package/dist/config.d.ts CHANGED
@@ -6,4 +6,5 @@ export type AuthStyle = 'tailwind' | 'hero-ui';
6
6
  export declare const pluginConfig: {
7
7
  style: AuthStyle;
8
8
  logoUrl?: string;
9
+ googleOAuthEnabled: boolean;
9
10
  };
package/dist/config.js CHANGED
@@ -2,5 +2,6 @@
2
2
  * Shared plugin configuration — set by the plugin factory at init time,
3
3
  * read by all client components at render time.
4
4
  */ export const pluginConfig = {
5
- style: 'tailwind'
5
+ style: 'tailwind',
6
+ googleOAuthEnabled: true
6
7
  };
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Google OAuth 2.0 — zero dependencies, pure REST.
3
+ *
4
+ * Flow:
5
+ * 1. GET /api/auth/oauth/google → Google consent screen
6
+ * 2. GET /api/auth/oauth/google/callback → exchange code, get profile, login, redirect
7
+ *
8
+ * Requires env vars: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
9
+ */ const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
10
+ const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
11
+ const GOOGLE_USERINFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo';
12
+ function env(key, fb = '') {
13
+ return process.env[key] || fb;
14
+ }
15
+ /**
16
+ * GET /api/auth/oauth/google — start Google OAuth flow
17
+ */ export const googleOAuthStart = {
18
+ path: '/api/auth/oauth/google',
19
+ method: 'get',
20
+ handler: async (req)=>{
21
+ const clientId = env('GOOGLE_CLIENT_ID');
22
+ if (!clientId) return Response.json({
23
+ error: 'Google OAuth not configured'
24
+ }, {
25
+ status: 501
26
+ });
27
+ const url = new URL(req.url || 'http://localhost');
28
+ const redirect = url.searchParams.get('redirect') || '/';
29
+ const base = env('NEXT_PUBLIC_SERVER_URL', 'http://localhost:3000');
30
+ const params = new URLSearchParams({
31
+ client_id: clientId,
32
+ redirect_uri: `${base}/api/auth/oauth/google/callback`,
33
+ response_type: 'code',
34
+ scope: 'openid email profile',
35
+ access_type: 'online',
36
+ state: encodeURIComponent(redirect)
37
+ });
38
+ return Response.redirect(`${GOOGLE_AUTH_URL}?${params.toString()}`, 302);
39
+ }
40
+ };
41
+ /**
42
+ * GET /api/auth/oauth/google/callback — handle Google's redirect back
43
+ */ export const googleOAuthCallback = {
44
+ path: '/api/auth/oauth/google/callback',
45
+ method: 'get',
46
+ handler: async (req)=>{
47
+ const url = new URL(req.url || 'http://localhost');
48
+ const code = url.searchParams.get('code');
49
+ const state = url.searchParams.get('state') || '/';
50
+ const redirectTo = decodeURIComponent(state);
51
+ if (!code) return Response.redirect(`/login?error=${url.searchParams.get('error') || 'oauth_failed'}`, 302);
52
+ const clientId = env('GOOGLE_CLIENT_ID');
53
+ const clientSecret = env('GOOGLE_CLIENT_SECRET');
54
+ const base = env('NEXT_PUBLIC_SERVER_URL', 'http://localhost:3000');
55
+ try {
56
+ // 1. Exchange code for access token
57
+ const tokenRes = await fetch(GOOGLE_TOKEN_URL, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/x-www-form-urlencoded'
61
+ },
62
+ body: new URLSearchParams({
63
+ code,
64
+ client_id: clientId,
65
+ client_secret: clientSecret,
66
+ redirect_uri: `${base}/api/auth/oauth/google/callback`,
67
+ grant_type: 'authorization_code'
68
+ })
69
+ });
70
+ if (!tokenRes.ok) return Response.redirect(`/login?error=oauth_failed`, 302);
71
+ const tokens = await tokenRes.json();
72
+ const accessToken = tokens.access_token;
73
+ // 2. Get Google profile
74
+ const profileRes = await fetch(GOOGLE_USERINFO_URL, {
75
+ headers: {
76
+ Authorization: `Bearer ${accessToken}`
77
+ }
78
+ });
79
+ if (!profileRes.ok) return Response.redirect(`/login?error=oauth_failed`, 302);
80
+ const profile = await profileRes.json();
81
+ const email = profile.email?.toLowerCase();
82
+ const name = profile.name || email?.split('@')[0];
83
+ if (!email) return Response.redirect(`/login?error=oauth_failed`, 302);
84
+ // 3. Find or create user
85
+ const users = await req.payload.find({
86
+ collection: 'users',
87
+ where: {
88
+ email: {
89
+ equals: email
90
+ }
91
+ },
92
+ limit: 1
93
+ });
94
+ let userId;
95
+ let userPw;
96
+ if (users.docs?.length) {
97
+ userId = users.docs[0].id;
98
+ // Set a known temp password so we can login
99
+ userPw = `g_tmp_${Date.now()}`;
100
+ await req.payload.update({
101
+ collection: 'users',
102
+ id: userId,
103
+ data: {
104
+ password: userPw
105
+ }
106
+ });
107
+ } else {
108
+ userPw = `g_new_${Date.now()}`;
109
+ const newUser = await req.payload.create({
110
+ collection: 'users',
111
+ data: {
112
+ email,
113
+ name,
114
+ password: userPw,
115
+ authProvider: 'google'
116
+ }
117
+ });
118
+ userId = newUser.id;
119
+ }
120
+ // 4. Login
121
+ const loginResult = await req.payload.login({
122
+ collection: 'users',
123
+ data: {
124
+ email,
125
+ password: userPw
126
+ },
127
+ req: req
128
+ });
129
+ // 5. Set cookie + redirect
130
+ const response = Response.redirect(redirectTo, 302);
131
+ if (loginResult.token) {
132
+ response.headers.set('Set-Cookie', `payload-token=${loginResult.token}; Path=/; HttpOnly; SameSite=Lax` + `${process.env.NODE_ENV === 'production' ? '; Secure' : ''}` + `; Max-Age=${loginResult.exp || 7200}`);
133
+ }
134
+ return response;
135
+ } catch (err) {
136
+ console.error('[auth-login] Google OAuth error:', err);
137
+ return Response.redirect(`/login?error=oauth_failed`, 302);
138
+ }
139
+ }
140
+ };
141
+ export const googleOAuthEndpoints = [
142
+ googleOAuthStart,
143
+ googleOAuthCallback
144
+ ];
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface AuthLoginPluginOptions {
7
7
  domain?: string;
8
8
  style?: AuthStyle;
9
9
  logo?: string;
10
- /** Enable Google OAuth — reads GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET from env */
10
+ /** Enable Google OAuth. Default: auto-detects GOOGLE_CLIENT_ID env var. Set false to force-disable. */
11
11
  googleOAuth?: boolean;
12
12
  }
13
13
  export declare const authLoginPlugin: (options?: AuthLoginPluginOptions) => (config: Config) => Config;
package/dist/index.js CHANGED
@@ -1,20 +1,35 @@
1
1
  import { authEndpoints } from './endpoints/authEndpoints.js';
2
+ import { googleOAuthEndpoints } from './endpoints/googleOAuth.js';
2
3
  import { pluginConfig } from './config.js';
3
4
  export const authLoginPlugin = (options = {})=>(config)=>{
4
5
  if (options.enabled === false) return config;
5
- // Set global style config — all page components read this at render time
6
+ // Set global config — all components read this at render time
6
7
  pluginConfig.style = options.style || 'tailwind';
7
8
  pluginConfig.logoUrl = options.logo;
9
+ // Google OAuth: auto-detect from env vars
10
+ if (options.googleOAuth === false) {
11
+ pluginConfig.googleOAuthEnabled = false;
12
+ } else {
13
+ const hasGoogleCreds = !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET);
14
+ pluginConfig.googleOAuthEnabled = options.googleOAuth === true || hasGoogleCreds;
15
+ }
8
16
  // Register auth API endpoints
9
17
  config.endpoints = [
10
18
  ...config.endpoints || [],
11
19
  ...authEndpoints
12
20
  ];
21
+ // Register Google OAuth endpoints only if enabled
22
+ if (pluginConfig.googleOAuthEnabled) {
23
+ config.endpoints = [
24
+ ...config.endpoints,
25
+ ...googleOAuthEndpoints
26
+ ];
27
+ }
13
28
  // Chain onInit
14
29
  const incomingOnInit = config.onInit;
15
30
  config.onInit = async (payload)=>{
16
31
  if (incomingOnInit) await incomingOnInit(payload);
17
- payload.logger.info(`[auth-login] Initialized (style: ${pluginConfig.style}) for ${options.projectName || 'project'}`);
32
+ payload.logger.info(`[auth-login] Initialized (style: ${pluginConfig.style}, googleOAuth: ${pluginConfig.googleOAuthEnabled}) for ${options.projectName || 'project'}`);
18
33
  };
19
34
  return config;
20
35
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@main12/auth-login",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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",