@main12/auth-login 0.1.6 → 0.1.7

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.
@@ -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.js CHANGED
@@ -1,4 +1,5 @@
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;
@@ -10,6 +11,13 @@ export const authLoginPlugin = (options = {})=>(config)=>{
10
11
  ...config.endpoints || [],
11
12
  ...authEndpoints
12
13
  ];
14
+ // Register Google OAuth endpoints if enabled
15
+ if (options.googleOAuth !== false) {
16
+ config.endpoints = [
17
+ ...config.endpoints,
18
+ ...googleOAuthEndpoints
19
+ ];
20
+ }
13
21
  // Chain onInit
14
22
  const incomingOnInit = config.onInit;
15
23
  config.onInit = async (payload)=>{
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.7",
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",