@meridianjs/auth 0.1.5 → 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
@@ -122,6 +122,85 @@ 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
206
  return import_jsonwebtoken.default.verify(token, secret, { algorithms: ["HS256"] });
package/dist/index.mjs CHANGED
@@ -82,6 +82,85 @@ 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
166
  return jwt.verify(token, secret, { algorithms: ["HS256"] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridianjs/auth",
3
- "version": "0.1.5",
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",