@meridianjs/auth 0.1.4 → 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.
package/dist/index.d.mts CHANGED
@@ -32,6 +32,20 @@ interface JwtPayload {
32
32
  iat?: number;
33
33
  exp?: number;
34
34
  }
35
+ interface GoogleAuthInput {
36
+ googleId: string;
37
+ email: string;
38
+ firstName: string | null;
39
+ lastName: string | null;
40
+ picture: string | null;
41
+ inviteRecord?: {
42
+ id: string;
43
+ email: string | null;
44
+ role: string;
45
+ workspace_id: string;
46
+ app_role_id: string | null;
47
+ } | null;
48
+ }
35
49
  declare const AuthModuleService_base: new (container: MeridianContainer) => _meridianjs_types.IModuleService;
36
50
  declare class AuthModuleService extends AuthModuleService_base {
37
51
  private readonly container;
@@ -40,6 +54,13 @@ declare class AuthModuleService extends AuthModuleService_base {
40
54
  register(input: RegisterInput): Promise<AuthResult>;
41
55
  /** Authenticate with email + password and return a signed JWT. */
42
56
  login(input: LoginInput): Promise<AuthResult>;
57
+ /**
58
+ * Sign in or register a user via Google OAuth.
59
+ * 1. Look up by google_id — existing SSO user
60
+ * 2. Look up by email — link google_id to existing account
61
+ * 3. Create new user
62
+ */
63
+ loginOrRegisterWithGoogle(input: GoogleAuthInput): Promise<AuthResult>;
43
64
  /** Verify a JWT and return its decoded payload. Throws if invalid or expired. */
44
65
  verifyToken(token: string, secret: string): JwtPayload;
45
66
  /** Resolve permissions for a given app_role_id — gracefully degrades if module not loaded. */
@@ -93,4 +114,4 @@ declare function requireWorkspace(req: any, res: Response, next: NextFunction):
93
114
  declare const AUTH_MODULE = "authModuleService";
94
115
  declare const _default: _meridianjs_types.ModuleDefinition;
95
116
 
96
- export { AUTH_MODULE, AuthModuleService, type AuthResult, type JwtPayload, type LoginInput, type RegisterInput, authenticateJWT, _default as default, requirePermission, requireRoles, requireWorkspace };
117
+ export { AUTH_MODULE, AuthModuleService, type AuthResult, type GoogleAuthInput, type JwtPayload, type LoginInput, type RegisterInput, authenticateJWT, _default as default, requirePermission, requireRoles, requireWorkspace };
package/dist/index.d.ts CHANGED
@@ -32,6 +32,20 @@ interface JwtPayload {
32
32
  iat?: number;
33
33
  exp?: number;
34
34
  }
35
+ interface GoogleAuthInput {
36
+ googleId: string;
37
+ email: string;
38
+ firstName: string | null;
39
+ lastName: string | null;
40
+ picture: string | null;
41
+ inviteRecord?: {
42
+ id: string;
43
+ email: string | null;
44
+ role: string;
45
+ workspace_id: string;
46
+ app_role_id: string | null;
47
+ } | null;
48
+ }
35
49
  declare const AuthModuleService_base: new (container: MeridianContainer) => _meridianjs_types.IModuleService;
36
50
  declare class AuthModuleService extends AuthModuleService_base {
37
51
  private readonly container;
@@ -40,6 +54,13 @@ declare class AuthModuleService extends AuthModuleService_base {
40
54
  register(input: RegisterInput): Promise<AuthResult>;
41
55
  /** Authenticate with email + password and return a signed JWT. */
42
56
  login(input: LoginInput): Promise<AuthResult>;
57
+ /**
58
+ * Sign in or register a user via Google OAuth.
59
+ * 1. Look up by google_id — existing SSO user
60
+ * 2. Look up by email — link google_id to existing account
61
+ * 3. Create new user
62
+ */
63
+ loginOrRegisterWithGoogle(input: GoogleAuthInput): Promise<AuthResult>;
43
64
  /** Verify a JWT and return its decoded payload. Throws if invalid or expired. */
44
65
  verifyToken(token: string, secret: string): JwtPayload;
45
66
  /** Resolve permissions for a given app_role_id — gracefully degrades if module not loaded. */
@@ -93,4 +114,4 @@ declare function requireWorkspace(req: any, res: Response, next: NextFunction):
93
114
  declare const AUTH_MODULE = "authModuleService";
94
115
  declare const _default: _meridianjs_types.ModuleDefinition;
95
116
 
96
- export { AUTH_MODULE, AuthModuleService, type AuthResult, type JwtPayload, type LoginInput, type RegisterInput, authenticateJWT, _default as default, requirePermission, requireRoles, requireWorkspace };
117
+ export { AUTH_MODULE, AuthModuleService, type AuthResult, type GoogleAuthInput, type JwtPayload, type LoginInput, type RegisterInput, authenticateJWT, _default as default, requirePermission, requireRoles, requireWorkspace };
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ var AuthModuleService = class extends (0, import_framework_utils.MeridianService
66
66
  const password_hash = await import_bcrypt.default.hash(input.password, BCRYPT_ROUNDS);
67
67
  let role = input.role ?? "member";
68
68
  if (!input.role) {
69
- const userCount = await userService.countUsers();
69
+ const [, userCount] = await userService.listAndCountUsers({}, { limit: 1 });
70
70
  if (userCount === 0) role = "super-admin";
71
71
  }
72
72
  const user = await userService.createUser({
@@ -122,9 +122,88 @@ var AuthModuleService = class extends (0, import_framework_utils.MeridianService
122
122
  token
123
123
  };
124
124
  }
125
+ /**
126
+ * Sign in or register a user via Google OAuth.
127
+ * 1. Look up by google_id — existing SSO user
128
+ * 2. Look up by email — link google_id to existing account
129
+ * 3. Create new user
130
+ */
131
+ async loginOrRegisterWithGoogle(input) {
132
+ const userService = this.container.resolve("userModuleService");
133
+ const config = this.container.resolve("config");
134
+ let user = await userService.retrieveUserByGoogleId(input.googleId);
135
+ if (!user) {
136
+ const existingByEmail = await userService.retrieveUserByEmail(input.email.toLowerCase().trim());
137
+ if (existingByEmail) {
138
+ throw Object.assign(
139
+ new Error(
140
+ "An account with this email already exists. Please sign in with your password. You can link Google sign-in from your account settings afterwards."
141
+ ),
142
+ { status: 409 }
143
+ );
144
+ }
145
+ }
146
+ if (user) {
147
+ if (!user.is_active) {
148
+ throw Object.assign(new Error("Account deactivated"), { status: 403 });
149
+ }
150
+ await userService.recordLogin(user.id).catch(() => {
151
+ });
152
+ const permissions2 = await this.resolvePermissions(user.app_role_id);
153
+ const { token: token2, jti: jti2, expiresAt: expiresAt2 } = this.signToken(user.id, null, [user.role ?? "member"], permissions2, config.projectConfig.jwtSecret);
154
+ await userService.createSession(jti2, user.id, expiresAt2).catch(() => {
155
+ });
156
+ return {
157
+ user: { id: user.id, email: user.email, first_name: user.first_name ?? null, last_name: user.last_name ?? null },
158
+ token: token2
159
+ };
160
+ }
161
+ const invite = input.inviteRecord;
162
+ let role = "member";
163
+ if (invite) {
164
+ role = invite.role ?? "member";
165
+ } else {
166
+ const [, userCount] = await userService.listAndCountUsers({}, { limit: 1 });
167
+ if (userCount === 0) {
168
+ role = "super-admin";
169
+ } else {
170
+ throw Object.assign(
171
+ new Error("You are not authorized to access this application. Contact an admin for an invitation."),
172
+ { status: 403 }
173
+ );
174
+ }
175
+ }
176
+ const password_hash = await import_bcrypt.default.hash((0, import_crypto.randomUUID)(), 1);
177
+ const newUser = await userService.createUser({
178
+ email: input.email.toLowerCase().trim(),
179
+ password_hash,
180
+ first_name: input.firstName ?? null,
181
+ last_name: input.lastName ?? null,
182
+ role,
183
+ is_active: true,
184
+ google_id: input.googleId,
185
+ ...invite?.app_role_id ? { app_role_id: invite.app_role_id } : {}
186
+ });
187
+ if (invite) {
188
+ try {
189
+ const workspaceMemberService = this.container.resolve("workspaceMemberModuleService");
190
+ const wsRole = invite.role === "member" ? "member" : "admin";
191
+ await workspaceMemberService.ensureMember(invite.workspace_id, newUser.id, wsRole);
192
+ } catch {
193
+ }
194
+ }
195
+ const permissions = await this.resolvePermissions(newUser.app_role_id);
196
+ const { token, jti, expiresAt } = this.signToken(newUser.id, null, [newUser.role], permissions, config.projectConfig.jwtSecret);
197
+ await userService.createSession(jti, newUser.id, expiresAt).catch(() => {
198
+ });
199
+ return {
200
+ user: { id: newUser.id, email: newUser.email, first_name: newUser.first_name ?? null, last_name: newUser.last_name ?? null },
201
+ token
202
+ };
203
+ }
125
204
  /** Verify a JWT and return its decoded payload. Throws if invalid or expired. */
126
205
  verifyToken(token, secret) {
127
- return import_jsonwebtoken.default.verify(token, secret);
206
+ return import_jsonwebtoken.default.verify(token, secret, { algorithms: ["HS256"] });
128
207
  }
129
208
  /** Resolve permissions for a given app_role_id — gracefully degrades if module not loaded. */
130
209
  async resolvePermissions(appRoleId) {
@@ -166,7 +245,7 @@ function authenticateJWT(req, res, next) {
166
245
  return;
167
246
  }
168
247
  try {
169
- const payload = import_jsonwebtoken2.default.verify(token, config.projectConfig.jwtSecret);
248
+ const payload = import_jsonwebtoken2.default.verify(token, config.projectConfig.jwtSecret, { algorithms: ["HS256"] });
170
249
  if (payload.jti) {
171
250
  try {
172
251
  const scope = req.scope;
package/dist/index.mjs CHANGED
@@ -26,7 +26,7 @@ var AuthModuleService = class extends MeridianService({}) {
26
26
  const password_hash = await bcrypt.hash(input.password, BCRYPT_ROUNDS);
27
27
  let role = input.role ?? "member";
28
28
  if (!input.role) {
29
- const userCount = await userService.countUsers();
29
+ const [, userCount] = await userService.listAndCountUsers({}, { limit: 1 });
30
30
  if (userCount === 0) role = "super-admin";
31
31
  }
32
32
  const user = await userService.createUser({
@@ -82,9 +82,88 @@ var AuthModuleService = class extends MeridianService({}) {
82
82
  token
83
83
  };
84
84
  }
85
+ /**
86
+ * Sign in or register a user via Google OAuth.
87
+ * 1. Look up by google_id — existing SSO user
88
+ * 2. Look up by email — link google_id to existing account
89
+ * 3. Create new user
90
+ */
91
+ async loginOrRegisterWithGoogle(input) {
92
+ const userService = this.container.resolve("userModuleService");
93
+ const config = this.container.resolve("config");
94
+ let user = await userService.retrieveUserByGoogleId(input.googleId);
95
+ if (!user) {
96
+ const existingByEmail = await userService.retrieveUserByEmail(input.email.toLowerCase().trim());
97
+ if (existingByEmail) {
98
+ throw Object.assign(
99
+ new Error(
100
+ "An account with this email already exists. Please sign in with your password. You can link Google sign-in from your account settings afterwards."
101
+ ),
102
+ { status: 409 }
103
+ );
104
+ }
105
+ }
106
+ if (user) {
107
+ if (!user.is_active) {
108
+ throw Object.assign(new Error("Account deactivated"), { status: 403 });
109
+ }
110
+ await userService.recordLogin(user.id).catch(() => {
111
+ });
112
+ const permissions2 = await this.resolvePermissions(user.app_role_id);
113
+ const { token: token2, jti: jti2, expiresAt: expiresAt2 } = this.signToken(user.id, null, [user.role ?? "member"], permissions2, config.projectConfig.jwtSecret);
114
+ await userService.createSession(jti2, user.id, expiresAt2).catch(() => {
115
+ });
116
+ return {
117
+ user: { id: user.id, email: user.email, first_name: user.first_name ?? null, last_name: user.last_name ?? null },
118
+ token: token2
119
+ };
120
+ }
121
+ const invite = input.inviteRecord;
122
+ let role = "member";
123
+ if (invite) {
124
+ role = invite.role ?? "member";
125
+ } else {
126
+ const [, userCount] = await userService.listAndCountUsers({}, { limit: 1 });
127
+ if (userCount === 0) {
128
+ role = "super-admin";
129
+ } else {
130
+ throw Object.assign(
131
+ new Error("You are not authorized to access this application. Contact an admin for an invitation."),
132
+ { status: 403 }
133
+ );
134
+ }
135
+ }
136
+ const password_hash = await bcrypt.hash(randomUUID(), 1);
137
+ const newUser = await userService.createUser({
138
+ email: input.email.toLowerCase().trim(),
139
+ password_hash,
140
+ first_name: input.firstName ?? null,
141
+ last_name: input.lastName ?? null,
142
+ role,
143
+ is_active: true,
144
+ google_id: input.googleId,
145
+ ...invite?.app_role_id ? { app_role_id: invite.app_role_id } : {}
146
+ });
147
+ if (invite) {
148
+ try {
149
+ const workspaceMemberService = this.container.resolve("workspaceMemberModuleService");
150
+ const wsRole = invite.role === "member" ? "member" : "admin";
151
+ await workspaceMemberService.ensureMember(invite.workspace_id, newUser.id, wsRole);
152
+ } catch {
153
+ }
154
+ }
155
+ const permissions = await this.resolvePermissions(newUser.app_role_id);
156
+ const { token, jti, expiresAt } = this.signToken(newUser.id, null, [newUser.role], permissions, config.projectConfig.jwtSecret);
157
+ await userService.createSession(jti, newUser.id, expiresAt).catch(() => {
158
+ });
159
+ return {
160
+ user: { id: newUser.id, email: newUser.email, first_name: newUser.first_name ?? null, last_name: newUser.last_name ?? null },
161
+ token
162
+ };
163
+ }
85
164
  /** Verify a JWT and return its decoded payload. Throws if invalid or expired. */
86
165
  verifyToken(token, secret) {
87
- return jwt.verify(token, secret);
166
+ return jwt.verify(token, secret, { algorithms: ["HS256"] });
88
167
  }
89
168
  /** Resolve permissions for a given app_role_id — gracefully degrades if module not loaded. */
90
169
  async resolvePermissions(appRoleId) {
@@ -126,7 +205,7 @@ function authenticateJWT(req, res, next) {
126
205
  return;
127
206
  }
128
207
  try {
129
- const payload = jwt2.verify(token, config.projectConfig.jwtSecret);
208
+ const payload = jwt2.verify(token, config.projectConfig.jwtSecret, { algorithms: ["HS256"] });
130
209
  if (payload.jti) {
131
210
  try {
132
211
  const scope = req.scope;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridianjs/auth",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "Meridian auth module — JWT authentication and middleware",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",