nuxt-users 1.34.7 → 1.36.0

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 (58) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/cli.mjs +111 -0
  3. package/dist/module.d.mts +1 -1
  4. package/dist/module.d.ts +1 -1
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +38 -3
  7. package/dist/runtime/assets/nuxt-users.css +1 -1
  8. package/dist/runtime/components/NUsersEmailConfirmation.d.vue.ts +38 -0
  9. package/dist/runtime/components/NUsersEmailConfirmation.vue.d.ts +3 -2
  10. package/dist/runtime/components/NUsersGoogleLoginButton.d.vue.ts +31 -0
  11. package/dist/runtime/components/NUsersGoogleLoginButton.vue +75 -0
  12. package/dist/runtime/components/NUsersGoogleLoginButton.vue.d.ts +31 -0
  13. package/dist/runtime/components/NUsersList.d.vue.ts +0 -0
  14. package/dist/runtime/components/NUsersLoginForm.d.vue.ts +48 -0
  15. package/dist/runtime/components/NUsersLoginForm.vue.d.ts +5 -4
  16. package/dist/runtime/components/NUsersLogoutLink.d.vue.ts +38 -0
  17. package/dist/runtime/components/NUsersLogoutLink.vue.d.ts +5 -4
  18. package/dist/runtime/components/NUsersPasswordStrengthIndicator.d.vue.ts +13 -0
  19. package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue.d.ts +2 -1
  20. package/dist/runtime/components/NUsersProfileInfo.d.vue.ts +3 -0
  21. package/dist/runtime/components/NUsersProfileInfo.vue.d.ts +2 -1
  22. package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +61 -0
  23. package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +5 -4
  24. package/dist/runtime/components/NUsersResetPasswordForm.d.vue.ts +18 -0
  25. package/dist/runtime/components/NUsersResetPasswordForm.vue.d.ts +4 -3
  26. package/dist/runtime/components/NUsersUserCard.d.vue.ts +44 -0
  27. package/dist/runtime/components/NUsersUserCard.vue.d.ts +3 -2
  28. package/dist/runtime/components/NUsersUserForm.d.vue.ts +15 -0
  29. package/dist/runtime/components/NUsersUserForm.vue.d.ts +2 -1
  30. package/dist/runtime/composables/useAuthentication.d.ts +1 -1
  31. package/dist/runtime/composables/useAuthentication.js +26 -8
  32. package/dist/runtime/middleware/authorization.client.js +9 -7
  33. package/dist/runtime/server/api/nuxt-users/auth/google/callback.get.d.ts +2 -0
  34. package/dist/runtime/server/api/nuxt-users/auth/google/callback.get.js +68 -0
  35. package/dist/runtime/server/api/nuxt-users/auth/google/redirect.get.d.ts +2 -0
  36. package/dist/runtime/server/api/nuxt-users/auth/google/redirect.get.js +39 -0
  37. package/dist/runtime/server/api/nuxt-users/inactive.get.js +1 -1
  38. package/dist/runtime/server/api/nuxt-users/index.get.js +1 -1
  39. package/dist/runtime/server/api/nuxt-users/session/index.post.d.ts +1 -1
  40. package/dist/runtime/server/api/nuxt-users/session/index.post.js +6 -0
  41. package/dist/runtime/server/services/password.js +8 -0
  42. package/dist/runtime/server/services/registration.js +14 -1
  43. package/dist/runtime/server/utils/add-google-oauth-fields.d.ts +2 -0
  44. package/dist/runtime/server/utils/add-google-oauth-fields.js +22 -0
  45. package/dist/runtime/server/utils/create-users-table.js +7 -1
  46. package/dist/runtime/server/utils/google-oauth.d.ts +33 -0
  47. package/dist/runtime/server/utils/google-oauth.js +142 -0
  48. package/dist/runtime/server/utils/index.d.ts +1 -0
  49. package/dist/runtime/server/utils/index.js +1 -0
  50. package/dist/runtime/server/utils/migrate.js +5 -0
  51. package/dist/runtime/server/utils/user.js +24 -3
  52. package/dist/shared/{nuxt-users.BTVH9EyH.d.mts → nuxt-users.SXaMnaEL.d.mts} +45 -1
  53. package/dist/shared/{nuxt-users.BTVH9EyH.d.ts → nuxt-users.SXaMnaEL.d.ts} +45 -1
  54. package/dist/utils.d.mts +1 -1
  55. package/dist/utils.d.ts +1 -1
  56. package/package.json +8 -6
  57. package/scripts/check-peer-dependencies.js +0 -0
  58. package/scripts/post-build.js +0 -0
@@ -32,6 +32,9 @@ export const registerUser = async (userData, options, baseUrl) => {
32
32
  throw new Error("Failed to create user.");
33
33
  }
34
34
  const user = result.rows[0];
35
+ if (!user) {
36
+ throw new Error("Failed to retrieve created user.");
37
+ }
35
38
  const passwordResetTokensTable = options.tables.passwordResetTokens;
36
39
  await db.sql`
37
40
  INSERT INTO {${passwordResetTokensTable}} (email, token, created_at)
@@ -149,8 +152,16 @@ export const confirmUserEmail = async (token, email, options) => {
149
152
  const currentTimeString = now.toISOString().slice(0, 19).replace("T", " ");
150
153
  const createdAtString = validTokenRecord.created_at instanceof Date ? validTokenRecord.created_at.toISOString().slice(0, 19).replace("T", " ") : String(validTokenRecord.created_at);
151
154
  const [datePart, timePart] = createdAtString.split(/[ T]/);
155
+ if (!datePart || !timePart) {
156
+ console.log(`[Nuxt Users] Invalid timestamp format for token: ${createdAtString}`);
157
+ return false;
158
+ }
152
159
  const [year, month, day] = datePart.split("-").map(Number);
153
160
  const [hour, minute, second] = timePart.split(":").map(Number);
161
+ if (!year || !month || !day || hour === void 0 || minute === void 0 || second === void 0) {
162
+ console.log("[Nuxt Users] Invalid timestamp components");
163
+ return false;
164
+ }
154
165
  let expirationHour = hour + TOKEN_EXPIRATION_HOURS;
155
166
  let expirationDay = day;
156
167
  let expirationMonth = month;
@@ -162,7 +173,9 @@ export const confirmUserEmail = async (token, email, options) => {
162
173
  if (expirationYear % 4 === 0 && (expirationYear % 100 !== 0 || expirationYear % 400 === 0)) {
163
174
  daysInMonth[1] = 29;
164
175
  }
165
- if (expirationDay > daysInMonth[expirationMonth - 1]) {
176
+ const monthIndex = expirationMonth - 1;
177
+ const daysInCurrentMonth = daysInMonth[monthIndex];
178
+ if (daysInCurrentMonth && expirationDay > daysInCurrentMonth) {
166
179
  expirationDay = 1;
167
180
  expirationMonth++;
168
181
  if (expirationMonth > 12) {
@@ -0,0 +1,2 @@
1
+ import type { ModuleOptions } from 'nuxt-users/utils';
2
+ export declare const addGoogleOauthFields: (options: ModuleOptions) => Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { useDb } from "./db.js";
2
+ export const addGoogleOauthFields = async (options) => {
3
+ const connectorName = options.connector.name;
4
+ const db = await useDb(options);
5
+ const tableName = options.tables.users;
6
+ console.log(`[Nuxt Users] DB:Add Google OAuth fields to ${connectorName} Users Table in ${tableName}...`);
7
+ if (connectorName === "sqlite" || connectorName === "mysql" || connectorName === "postgresql") {
8
+ try {
9
+ await db.sql`ALTER TABLE {${tableName}} ADD COLUMN google_id TEXT`;
10
+ console.log("[Nuxt Users] Added google_id column \u2705");
11
+ } catch (error) {
12
+ console.error("[Nuxt Users] google_id column might already exist", error);
13
+ }
14
+ try {
15
+ await db.sql`ALTER TABLE {${tableName}} ADD COLUMN profile_picture TEXT`;
16
+ console.log("[Nuxt Users] Added profile_picture column \u2705");
17
+ } catch (error) {
18
+ console.error("[Nuxt Users] profile_picture column might already exist", error);
19
+ }
20
+ }
21
+ console.log(`[Nuxt Users] DB:Add Google OAuth fields to ${connectorName} Users Table \u2705`);
22
+ };
@@ -12,6 +12,8 @@ export const createUsersTable = async (options) => {
12
12
  name TEXT NOT NULL,
13
13
  password TEXT NOT NULL,
14
14
  role TEXT NOT NULL DEFAULT 'user',
15
+ google_id TEXT UNIQUE,
16
+ profile_picture TEXT,
15
17
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
16
18
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
17
19
  )
@@ -25,6 +27,8 @@ export const createUsersTable = async (options) => {
25
27
  name VARCHAR(255) NOT NULL,
26
28
  password VARCHAR(255) NOT NULL,
27
29
  role VARCHAR(32) NOT NULL DEFAULT 'user',
30
+ google_id VARCHAR(255) UNIQUE,
31
+ profile_picture TEXT,
28
32
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
29
33
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
30
34
  )
@@ -38,10 +42,12 @@ export const createUsersTable = async (options) => {
38
42
  name VARCHAR(255) NOT NULL,
39
43
  password VARCHAR(255) NOT NULL,
40
44
  role VARCHAR(32) NOT NULL DEFAULT 'user',
45
+ google_id VARCHAR(255) UNIQUE,
46
+ profile_picture TEXT,
41
47
  created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
42
48
  updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
43
49
  )
44
50
  `;
45
51
  }
46
- console.log(`[Nuxt Users] DB:Create ${connectorName} Users Table Fields: id, email, name, password, role, created_at, updated_at \u2705`);
52
+ console.log(`[Nuxt Users] DB:Create ${connectorName} Users Table Fields: id, email, name, password, role, google_id, profile_picture, created_at, updated_at \u2705`);
47
53
  };
@@ -0,0 +1,33 @@
1
+ import { OAuth2Client } from 'google-auth-library';
2
+ import type { ModuleOptions, User, GoogleOAuthOptions } from 'nuxt-users/utils';
3
+ export interface GoogleUserInfo {
4
+ id: string;
5
+ email: string;
6
+ name: string;
7
+ picture?: string;
8
+ verified_email: boolean;
9
+ }
10
+ /**
11
+ * Create Google OAuth2 client
12
+ */
13
+ export declare function createGoogleOAuth2Client(options: GoogleOAuthOptions, callbackUrl: string): OAuth2Client;
14
+ /**
15
+ * Generate authorization URL for Google OAuth
16
+ */
17
+ export declare function getGoogleAuthUrl(oauth2Client: OAuth2Client, options: GoogleOAuthOptions): string;
18
+ /**
19
+ * Exchange authorization code for tokens and get user info
20
+ */
21
+ export declare function getGoogleUserFromCode(oauth2Client: OAuth2Client, code: string): Promise<GoogleUserInfo>;
22
+ /**
23
+ * Generate a secure random password for OAuth users
24
+ */
25
+ export declare function generateSecurePassword(): Promise<string>;
26
+ /**
27
+ * Find or create user from Google OAuth data
28
+ */
29
+ export declare function findOrCreateGoogleUser(googleUser: GoogleUserInfo, moduleOptions: ModuleOptions): Promise<User | null>;
30
+ /**
31
+ * Create authentication token for user (reusing existing logic)
32
+ */
33
+ export declare function createAuthTokenForUser(user: User, moduleOptions: ModuleOptions, rememberMe?: boolean): Promise<string>;
@@ -0,0 +1,142 @@
1
+ import { OAuth2Client } from "google-auth-library";
2
+ import crypto from "node:crypto";
3
+ import bcrypt from "bcrypt";
4
+ import { useDb } from "./db.js";
5
+ export function createGoogleOAuth2Client(options, callbackUrl) {
6
+ return new OAuth2Client(
7
+ options.clientId,
8
+ options.clientSecret,
9
+ callbackUrl
10
+ );
11
+ }
12
+ export function getGoogleAuthUrl(oauth2Client, options) {
13
+ const scopes = options.scopes || ["openid", "profile", "email"];
14
+ return oauth2Client.generateAuthUrl({
15
+ access_type: "offline",
16
+ scope: scopes,
17
+ include_granted_scopes: true
18
+ });
19
+ }
20
+ export async function getGoogleUserFromCode(oauth2Client, code) {
21
+ const { tokens } = await oauth2Client.getToken(code);
22
+ oauth2Client.setCredentials(tokens);
23
+ const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
24
+ headers: {
25
+ Authorization: `Bearer ${tokens.access_token}`
26
+ }
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(`Failed to fetch user info: ${response.statusText}`);
30
+ }
31
+ const data = await response.json();
32
+ if (!data.email || !data.verified_email) {
33
+ throw new Error("Google account email not verified");
34
+ }
35
+ return {
36
+ id: data.id,
37
+ email: data.email,
38
+ name: data.name || data.email.split("@")[0],
39
+ picture: data.picture || void 0,
40
+ verified_email: data.verified_email
41
+ };
42
+ }
43
+ export async function generateSecurePassword() {
44
+ const randomBytes = crypto.randomBytes(32).toString("hex");
45
+ return await bcrypt.hash(`OAUTH_${randomBytes}`, 10);
46
+ }
47
+ export async function findOrCreateGoogleUser(googleUser, moduleOptions) {
48
+ const db = await useDb(moduleOptions);
49
+ const usersTable = moduleOptions.tables.users;
50
+ const allowAutoRegistration = moduleOptions.auth.google?.allowAutoRegistration ?? false;
51
+ let userResult = await db.sql`
52
+ SELECT * FROM {${usersTable}}
53
+ WHERE google_id = ${googleUser.id}
54
+ `;
55
+ if (userResult.rows.length > 0) {
56
+ const user = userResult.rows[0];
57
+ if (!user) {
58
+ return null;
59
+ }
60
+ if (user.profile_picture !== googleUser.picture) {
61
+ await db.sql`
62
+ UPDATE {${usersTable}}
63
+ SET profile_picture = ${googleUser.picture}, updated_at = CURRENT_TIMESTAMP
64
+ WHERE id = ${user.id}
65
+ `;
66
+ user.profile_picture = googleUser.picture;
67
+ }
68
+ return user;
69
+ }
70
+ userResult = await db.sql`
71
+ SELECT * FROM {${usersTable}}
72
+ WHERE email = ${googleUser.email}
73
+ `;
74
+ if (userResult.rows.length > 0) {
75
+ const user = userResult.rows[0];
76
+ if (!user) {
77
+ return null;
78
+ }
79
+ await db.sql`
80
+ UPDATE {${usersTable}}
81
+ SET google_id = ${googleUser.id},
82
+ profile_picture = ${googleUser.picture},
83
+ updated_at = CURRENT_TIMESTAMP
84
+ WHERE id = ${user.id}
85
+ `;
86
+ user.google_id = googleUser.id;
87
+ user.profile_picture = googleUser.picture;
88
+ return user;
89
+ }
90
+ if (!allowAutoRegistration) {
91
+ return null;
92
+ }
93
+ const securePassword = await generateSecurePassword();
94
+ await db.sql`
95
+ INSERT INTO {${usersTable}} (
96
+ email, name, password, role, google_id, profile_picture,
97
+ active, created_at, updated_at
98
+ ) VALUES (
99
+ ${googleUser.email},
100
+ ${googleUser.name},
101
+ ${securePassword},
102
+ 'user',
103
+ ${googleUser.id},
104
+ ${googleUser.picture || null},
105
+ true,
106
+ CURRENT_TIMESTAMP,
107
+ CURRENT_TIMESTAMP
108
+ )
109
+ `;
110
+ const createdUserResult = await db.sql`
111
+ SELECT * FROM {${usersTable}}
112
+ WHERE google_id = ${googleUser.id}
113
+ `;
114
+ return createdUserResult.rows[0] ?? null;
115
+ }
116
+ export async function createAuthTokenForUser(user, moduleOptions, rememberMe = false) {
117
+ const db = await useDb(moduleOptions);
118
+ const personalAccessTokensTable = moduleOptions.tables.personalAccessTokens;
119
+ const token = crypto.randomBytes(64).toString("hex");
120
+ const tokenName = "oauth_auth_token";
121
+ const expiresAt = /* @__PURE__ */ new Date();
122
+ if (rememberMe) {
123
+ const longTermDays = moduleOptions.auth.rememberMeExpiration || 30;
124
+ expiresAt.setDate(expiresAt.getDate() + longTermDays);
125
+ } else {
126
+ expiresAt.setMinutes(expiresAt.getMinutes() + moduleOptions.auth.tokenExpiration);
127
+ }
128
+ await db.sql`
129
+ INSERT INTO {${personalAccessTokensTable}} (
130
+ tokenable_type, tokenable_id, name, token, expires_at, created_at, updated_at
131
+ ) VALUES (
132
+ 'user',
133
+ ${user.id},
134
+ ${tokenName},
135
+ ${token},
136
+ ${expiresAt.toISOString().slice(0, 19).replace("T", " ")},
137
+ CURRENT_TIMESTAMP,
138
+ CURRENT_TIMESTAMP
139
+ )
140
+ `;
141
+ return token;
142
+ }
@@ -6,3 +6,4 @@ export { createPersonalAccessTokensTable } from './create-personal-access-tokens
6
6
  export { createPasswordResetTokensTable } from './create-password-reset-tokens-table.js';
7
7
  export { createMigrationsTable } from './create-migrations-table.js';
8
8
  export { runMigrations } from './migrate.js';
9
+ export { createGoogleOAuth2Client, getGoogleAuthUrl, getGoogleUserFromCode, findOrCreateGoogleUser, createAuthTokenForUser, generateSecurePassword } from './google-oauth.js';
@@ -6,3 +6,4 @@ export { createPersonalAccessTokensTable } from "./create-personal-access-tokens
6
6
  export { createPasswordResetTokensTable } from "./create-password-reset-tokens-table.js";
7
7
  export { createMigrationsTable } from "./create-migrations-table.js";
8
8
  export { runMigrations } from "./migrate.js";
9
+ export { createGoogleOAuth2Client, getGoogleAuthUrl, getGoogleUserFromCode, findOrCreateGoogleUser, createAuthTokenForUser, generateSecurePassword } from "./google-oauth.js";
@@ -4,6 +4,7 @@ import { createPersonalAccessTokensTable } from "./create-personal-access-tokens
4
4
  import { createPasswordResetTokensTable } from "./create-password-reset-tokens-table.js";
5
5
  import { createMigrationsTable } from "./create-migrations-table.js";
6
6
  import { addActiveToUsers } from "./add-active-to-users.js";
7
+ import { addGoogleOauthFields } from "./add-google-oauth-fields.js";
7
8
  const migrations = [
8
9
  {
9
10
  name: "create_migrations_table",
@@ -24,6 +25,10 @@ const migrations = [
24
25
  {
25
26
  name: "add_active_to_users",
26
27
  run: addActiveToUsers
28
+ },
29
+ {
30
+ name: "add_google_oauth_fields",
31
+ run: addGoogleOauthFields
27
32
  }
28
33
  ];
29
34
  export const getAppliedMigrations = async (options) => {
@@ -20,6 +20,9 @@ export const createUser = async (userData, options) => {
20
20
  throw new Error("Failed to retrieve created user.");
21
21
  }
22
22
  const user = result.rows[0];
23
+ if (!user) {
24
+ throw new Error("Failed to retrieve created user.");
25
+ }
23
26
  return {
24
27
  id: user.id,
25
28
  email: user.email,
@@ -38,6 +41,9 @@ export const findUserByEmail = async (email, options) => {
38
41
  return null;
39
42
  }
40
43
  const user = result.rows[0];
44
+ if (!user) {
45
+ return null;
46
+ }
41
47
  return {
42
48
  id: user.id,
43
49
  email: user.email,
@@ -57,6 +63,9 @@ export const findUserById = async (id, options, withPass = false) => {
57
63
  return null;
58
64
  }
59
65
  const user = result.rows[0];
66
+ if (!user) {
67
+ return null;
68
+ }
60
69
  const normalizedUser = {
61
70
  ...user,
62
71
  created_at: typeof user.created_at === "object" ? user.created_at.toISOString() : user.created_at,
@@ -126,7 +135,8 @@ export const hasAnyUsers = async (options) => {
126
135
  try {
127
136
  const db = await useDb(options);
128
137
  const users = await db.sql`SELECT COUNT(*) as count FROM {${options.tables.users}}`;
129
- return users.rows?.[0]?.count > 0;
138
+ const firstRow = users.rows?.[0];
139
+ return firstRow ? firstRow.count > 0 : false;
130
140
  } catch {
131
141
  return false;
132
142
  }
@@ -143,7 +153,11 @@ export const getCurrentUserFromToken = async (token, options, withPass = false)
143
153
  if (tokenResult.rows.length === 0) {
144
154
  return null;
145
155
  }
146
- const userId = tokenResult.rows[0].tokenable_id;
156
+ const tokenRow = tokenResult.rows[0];
157
+ if (!tokenRow) {
158
+ return null;
159
+ }
160
+ const userId = tokenRow.tokenable_id;
147
161
  await db.sql`
148
162
  UPDATE {${personalAccessTokensTable}}
149
163
  SET last_used_at = CURRENT_TIMESTAMP
@@ -157,6 +171,9 @@ export const getCurrentUserFromToken = async (token, options, withPass = false)
157
171
  return null;
158
172
  }
159
173
  const user = userResult.rows[0];
174
+ if (!user) {
175
+ return null;
176
+ }
160
177
  if (!user.active) {
161
178
  return null;
162
179
  }
@@ -217,6 +234,10 @@ export const getLastLoginTime = async (userId, options) => {
217
234
  if (result.rows.length === 0) {
218
235
  return null;
219
236
  }
220
- const lastLogin = result.rows[0].created_at;
237
+ const resultRow = result.rows[0];
238
+ if (!resultRow) {
239
+ return null;
240
+ }
241
+ const lastLogin = resultRow.created_at;
221
242
  return lastLogin instanceof Date ? lastLogin.toISOString() : lastLogin;
222
243
  };
@@ -12,6 +12,42 @@ type DatabaseConfig = {
12
12
  password?: string;
13
13
  database?: string;
14
14
  };
15
+ interface GoogleOAuthOptions {
16
+ /**
17
+ * Google OAuth client ID from Google Cloud Console
18
+ */
19
+ clientId: string;
20
+ /**
21
+ * Google OAuth client secret from Google Cloud Console
22
+ */
23
+ clientSecret: string;
24
+ /**
25
+ * Callback URL for Google OAuth (must match what's configured in Google Cloud Console)
26
+ * @default '/api/nuxt-users/auth/google/callback'
27
+ */
28
+ callbackUrl?: string;
29
+ /**
30
+ * Redirect URL after successful authentication
31
+ * @default '/'
32
+ */
33
+ successRedirect?: string;
34
+ /**
35
+ * Redirect URL after failed authentication
36
+ * @default '/login?error=oauth_failed'
37
+ */
38
+ errorRedirect?: string;
39
+ /**
40
+ * Google OAuth scopes to request
41
+ * @default ['openid', 'profile', 'email']
42
+ */
43
+ scopes?: string[];
44
+ /**
45
+ * Allow automatic user registration when logging in with Google for the first time
46
+ * If false, only existing users with matching email can log in with Google
47
+ * @default false
48
+ */
49
+ allowAutoRegistration?: boolean;
50
+ }
15
51
  interface RuntimeModuleOptions {
16
52
  connector?: {
17
53
  name: DatabaseType;
@@ -75,6 +111,11 @@ interface RuntimeModuleOptions {
75
111
  * }
76
112
  */
77
113
  permissions?: Record<string, (string | Permission)[]>;
114
+ /**
115
+ * Google OAuth configuration
116
+ * Enable Google OAuth login/registration
117
+ */
118
+ google?: GoogleOAuthOptions;
78
119
  };
79
120
  /**
80
121
  * Password validation configuration
@@ -139,6 +180,7 @@ interface ModuleOptions {
139
180
  tokenExpiration: number;
140
181
  rememberMeExpiration: number;
141
182
  permissions: Record<string, (string | Permission)[]>;
183
+ google?: GoogleOAuthOptions;
142
184
  };
143
185
  passwordValidation: {
144
186
  minLength: number;
@@ -169,6 +211,8 @@ interface User {
169
211
  password: string;
170
212
  role: string;
171
213
  active: boolean;
214
+ google_id?: string;
215
+ profile_picture?: string;
172
216
  created_at: string;
173
217
  updated_at: string;
174
218
  last_login_at?: string;
@@ -225,4 +269,4 @@ declare const defaultFieldLabels: {
225
269
  };
226
270
 
227
271
  export { defaultDisplayFields as h, defaultFieldLabels as i };
228
- export type { DisplayFieldsProps as D, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
272
+ export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
@@ -12,6 +12,42 @@ type DatabaseConfig = {
12
12
  password?: string;
13
13
  database?: string;
14
14
  };
15
+ interface GoogleOAuthOptions {
16
+ /**
17
+ * Google OAuth client ID from Google Cloud Console
18
+ */
19
+ clientId: string;
20
+ /**
21
+ * Google OAuth client secret from Google Cloud Console
22
+ */
23
+ clientSecret: string;
24
+ /**
25
+ * Callback URL for Google OAuth (must match what's configured in Google Cloud Console)
26
+ * @default '/api/nuxt-users/auth/google/callback'
27
+ */
28
+ callbackUrl?: string;
29
+ /**
30
+ * Redirect URL after successful authentication
31
+ * @default '/'
32
+ */
33
+ successRedirect?: string;
34
+ /**
35
+ * Redirect URL after failed authentication
36
+ * @default '/login?error=oauth_failed'
37
+ */
38
+ errorRedirect?: string;
39
+ /**
40
+ * Google OAuth scopes to request
41
+ * @default ['openid', 'profile', 'email']
42
+ */
43
+ scopes?: string[];
44
+ /**
45
+ * Allow automatic user registration when logging in with Google for the first time
46
+ * If false, only existing users with matching email can log in with Google
47
+ * @default false
48
+ */
49
+ allowAutoRegistration?: boolean;
50
+ }
15
51
  interface RuntimeModuleOptions {
16
52
  connector?: {
17
53
  name: DatabaseType;
@@ -75,6 +111,11 @@ interface RuntimeModuleOptions {
75
111
  * }
76
112
  */
77
113
  permissions?: Record<string, (string | Permission)[]>;
114
+ /**
115
+ * Google OAuth configuration
116
+ * Enable Google OAuth login/registration
117
+ */
118
+ google?: GoogleOAuthOptions;
78
119
  };
79
120
  /**
80
121
  * Password validation configuration
@@ -139,6 +180,7 @@ interface ModuleOptions {
139
180
  tokenExpiration: number;
140
181
  rememberMeExpiration: number;
141
182
  permissions: Record<string, (string | Permission)[]>;
183
+ google?: GoogleOAuthOptions;
142
184
  };
143
185
  passwordValidation: {
144
186
  minLength: number;
@@ -169,6 +211,8 @@ interface User {
169
211
  password: string;
170
212
  role: string;
171
213
  active: boolean;
214
+ google_id?: string;
215
+ profile_picture?: string;
172
216
  created_at: string;
173
217
  updated_at: string;
174
218
  last_login_at?: string;
@@ -225,4 +269,4 @@ declare const defaultFieldLabels: {
225
269
  };
226
270
 
227
271
  export { defaultDisplayFields as h, defaultFieldLabels as i };
228
- export type { DisplayFieldsProps as D, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
272
+ export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
package/dist/utils.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, h as defaultDisplayFields, i as defaultFieldLabels } from './shared/nuxt-users.BTVH9EyH.mjs';
1
+ export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, G as GoogleOAuthOptions, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, h as defaultDisplayFields, i as defaultFieldLabels } from './shared/nuxt-users.SXaMnaEL.mjs';
2
2
 
3
3
  interface PasswordValidationResult {
4
4
  isValid: boolean;
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, h as defaultDisplayFields, i as defaultFieldLabels } from './shared/nuxt-users.BTVH9EyH.js';
1
+ export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, G as GoogleOAuthOptions, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, h as defaultDisplayFields, i as defaultFieldLabels } from './shared/nuxt-users.SXaMnaEL.js';
2
2
 
3
3
  interface PasswordValidationResult {
4
4
  isValid: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-users",
3
- "version": "1.34.7",
3
+ "version": "1.36.0",
4
4
  "description": "A comprehensive user management module for Nuxt 3 and Nuxt 4 applications with authentication, authorization, database support, and CLI tools",
5
5
  "keywords": [
6
6
  "nuxt",
@@ -86,12 +86,13 @@
86
86
  "docs:dev": "vitepress dev docs",
87
87
  "docs:build": "vitepress build docs",
88
88
  "docs:preview": "vitepress preview docs",
89
- "docs:release": "yarn docs:build && ./scripts/deploy-docs.sh"
89
+ "docs:release": "date +%Y%m%d%H%M%S > docs/.lastRelease && yarn docs:build && ./scripts/deploy-docs.sh"
90
90
  },
91
91
  "dependencies": {
92
92
  "bcrypt": "^6.0.0",
93
93
  "citty": "^0.1.6",
94
94
  "defu": "^6.1.4",
95
+ "google-auth-library": "^10.4.0",
95
96
  "nodemailer": "^7.0.5"
96
97
  },
97
98
  "optionalDependencies": {
@@ -113,22 +114,23 @@
113
114
  "devDependencies": {
114
115
  "@nuxt/devtools": "^2.6.2",
115
116
  "@nuxt/eslint-config": "^1.5.2",
116
- "@nuxt/kit": "^3.17.6",
117
+ "@nuxt/kit": "^4.0.0",
117
118
  "@nuxt/module-builder": "^1.0.2",
118
- "@nuxt/schema": "^3.17.6",
119
+ "@nuxt/schema": "^4.0.0",
119
120
  "@nuxt/test-utils": "^3.19.2",
121
+ "@nuxt/vite-builder": "^4.2.0",
120
122
  "@types/bcrypt": "^6.0.0",
121
123
  "@types/node": "latest",
122
124
  "@types/nodemailer": "^6.4.17",
123
125
  "@types/pg": "^8.15.4",
124
126
  "changelogen": "^0.6.2",
125
127
  "eslint": "^9.30.1",
126
- "nuxt": "^3.17.6",
128
+ "nuxt": "^4.0.0",
127
129
  "tsx": "^4.20.3",
128
130
  "typescript": "~5.9.0",
129
131
  "unbuild": "^3.0.0",
130
132
  "vitepress": "^1.6.3",
131
- "vitest": "^3.2.4",
133
+ "vitest": "^4.0.0",
132
134
  "vue-tsc": "^3.0.1"
133
135
  }
134
136
  }
File without changes
File without changes