Skip to content

Commit 34a76fb

Browse files
committed
Update token service and guards
1 parent a8ccaa7 commit 34a76fb

12 files changed

+432
-248
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import {
2-
BadRequestException,
3-
ForbiddenException,
4-
InternalServerErrorException,
5-
NotFoundException,
6-
UseGuards,
7-
} from '@nestjs/common';
1+
import { UseGuards } from '@nestjs/common';
82
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
93
import { InjectRepository } from '@nestjs/typeorm';
104

@@ -25,15 +19,13 @@ import { UpdatePasswordViaResetTokenInput } from 'src/engine/core-modules/auth/d
2519
import { ValidatePasswordResetToken } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.entity';
2620
import { ValidatePasswordResetTokenInput } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.input';
2721
import { authGraphqlApiExceptionHandler } from 'src/engine/core-modules/auth/utils/auth-graphql-api-exception-handler.util';
28-
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
2922
import { UserService } from 'src/engine/core-modules/user/services/user.service';
3023
import { User } from 'src/engine/core-modules/user/user.entity';
3124
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
3225
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
3326
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
3427
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
3528
import { CaptchaGuard } from 'src/engine/integrations/captcha/captcha.guard';
36-
import { assert } from 'src/utils/assert';
3729

3830
import { ChallengeInput } from './dto/challenge.input';
3931
import { ImpersonateInput } from './dto/impersonate.input';
@@ -91,81 +83,99 @@ export class AuthResolver {
9183
@Query(() => Workspace)
9284
async findWorkspaceFromInviteHash(
9385
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
94-
): Promise<Workspace> {
95-
const workspace = await this.workspaceRepository.findOneBy({
96-
inviteHash: workspaceInviteHashValidInput.inviteHash,
97-
});
98-
99-
if (!workspace) {
100-
throw new NotFoundError('Workspace does not exist');
86+
): Promise<Workspace | void> {
87+
try {
88+
return await this.authService.findWorkspaceFromInviteHash(
89+
workspaceInviteHashValidInput.inviteHash,
90+
);
91+
} catch (error) {
92+
authGraphqlApiExceptionHandler(error);
10193
}
102-
103-
return workspace;
10494
}
10595

10696
@UseGuards(CaptchaGuard)
10797
@Mutation(() => LoginToken)
108-
async challenge(@Args() challengeInput: ChallengeInput): Promise<LoginToken> {
109-
const user = await this.authService.challenge(challengeInput);
110-
const loginToken = await this.tokenService.generateLoginToken(user.email);
98+
async challenge(
99+
@Args() challengeInput: ChallengeInput,
100+
): Promise<LoginToken | void> {
101+
try {
102+
const user = await this.authService.challenge(challengeInput);
103+
const loginToken = await this.tokenService.generateLoginToken(user.email);
111104

112-
return { loginToken };
105+
return { loginToken };
106+
} catch (error) {
107+
authGraphqlApiExceptionHandler(error);
108+
}
113109
}
114110

115111
@UseGuards(CaptchaGuard)
116112
@Mutation(() => LoginToken)
117-
async signUp(@Args() signUpInput: SignUpInput): Promise<LoginToken> {
118-
const user = await this.authService.signInUp({
119-
...signUpInput,
120-
fromSSO: false,
121-
});
113+
async signUp(@Args() signUpInput: SignUpInput): Promise<LoginToken | void> {
114+
try {
115+
const user = await this.authService.signInUp({
116+
...signUpInput,
117+
fromSSO: false,
118+
});
122119

123-
const loginToken = await this.tokenService.generateLoginToken(user.email);
120+
const loginToken = await this.tokenService.generateLoginToken(user.email);
124121

125-
return { loginToken };
122+
return { loginToken };
123+
} catch (error) {
124+
authGraphqlApiExceptionHandler(error);
125+
}
126126
}
127127

128128
@Mutation(() => ExchangeAuthCode)
129129
async exchangeAuthorizationCode(
130130
@Args() exchangeAuthCodeInput: ExchangeAuthCodeInput,
131131
) {
132-
const tokens = await this.tokenService.verifyAuthorizationCode(
133-
exchangeAuthCodeInput,
134-
);
132+
try {
133+
const tokens = await this.tokenService.verifyAuthorizationCode(
134+
exchangeAuthCodeInput,
135+
);
135136

136-
return tokens;
137+
return tokens;
138+
} catch (error) {
139+
authGraphqlApiExceptionHandler(error);
140+
}
137141
}
138142

139143
@Mutation(() => TransientToken)
140144
@UseGuards(JwtAuthGuard)
141145
async generateTransientToken(
142146
@AuthUser() user: User,
143147
): Promise<TransientToken | void> {
144-
const workspaceMember = await this.userService.loadWorkspaceMember(user);
148+
try {
149+
const workspaceMember = await this.userService.loadWorkspaceMember(user);
150+
151+
if (!workspaceMember) {
152+
return;
153+
}
154+
const transientToken = await this.tokenService.generateTransientToken(
155+
workspaceMember.id,
156+
user.id,
157+
user.defaultWorkspace.id,
158+
);
145159

146-
if (!workspaceMember) {
147-
return;
160+
return { transientToken };
161+
} catch (error) {
162+
authGraphqlApiExceptionHandler(error);
148163
}
149-
const transientToken = await this.tokenService.generateTransientToken(
150-
workspaceMember.id,
151-
user.id,
152-
user.defaultWorkspace.id,
153-
);
154-
155-
return { transientToken };
156164
}
157165

158166
@Mutation(() => Verify)
159-
async verify(@Args() verifyInput: VerifyInput): Promise<Verify> {
160-
const email = await this.tokenService.verifyLoginToken(
161-
verifyInput.loginToken,
162-
);
163-
164-
assert(email, 'Invalid token', ForbiddenException);
167+
async verify(@Args() verifyInput: VerifyInput): Promise<Verify | void> {
168+
try {
169+
const email = await this.tokenService.verifyLoginToken(
170+
verifyInput.loginToken,
171+
);
165172

166-
const result = await this.authService.verify(email);
173+
const result = await this.authService.verify(email);
167174

168-
return result;
175+
return result;
176+
} catch (error) {
177+
authGraphqlApiExceptionHandler(error);
178+
}
169179
}
170180

171181
@Mutation(() => AuthorizeApp)
@@ -191,35 +201,40 @@ export class AuthResolver {
191201
async generateJWT(
192202
@AuthUser() user: User,
193203
@Args() args: GenerateJwtInput,
194-
): Promise<AuthTokens> {
195-
const token = await this.tokenService.generateSwitchWorkspaceToken(
196-
user,
197-
args.workspaceId,
198-
);
204+
): Promise<AuthTokens | void> {
205+
try {
206+
const token = await this.tokenService.generateSwitchWorkspaceToken(
207+
user,
208+
args.workspaceId,
209+
);
199210

200-
return token;
211+
return token;
212+
} catch (error) {
213+
authGraphqlApiExceptionHandler(error);
214+
}
201215
}
202216

203217
@Mutation(() => AuthTokens)
204-
async renewToken(@Args() args: AppTokenInput): Promise<AuthTokens> {
205-
if (!args.appToken) {
206-
throw new BadRequestException('Refresh token is mendatory');
207-
}
208-
209-
const tokens = await this.tokenService.generateTokensFromRefreshToken(
210-
args.appToken,
211-
);
218+
async renewToken(@Args() args: AppTokenInput): Promise<AuthTokens | void> {
219+
try {
220+
const tokens = await this.tokenService.generateTokensFromRefreshToken(
221+
args.appToken,
222+
);
212223

213-
return { tokens: tokens };
224+
return { tokens: tokens };
225+
} catch (error) {
226+
authGraphqlApiExceptionHandler(error);
227+
}
214228
}
215229

216230
@UseGuards(JwtAuthGuard)
217231
@Mutation(() => Verify)
218232
async impersonate(
219233
@Args() impersonateInput: ImpersonateInput,
234+
@AuthUser() user: User,
220235
): Promise<Verify | void> {
221236
try {
222-
return await this.authService.impersonate(impersonateInput.userId);
237+
return await this.authService.impersonate(impersonateInput.userId, user);
223238
} catch (error) {
224239
authGraphqlApiExceptionHandler(error);
225240
}
@@ -231,53 +246,62 @@ export class AuthResolver {
231246
@Args() args: ApiKeyTokenInput,
232247
@AuthWorkspace() { id: workspaceId }: Workspace,
233248
): Promise<ApiKeyToken | undefined> {
234-
return await this.tokenService.generateApiKeyToken(
235-
workspaceId,
236-
args.apiKeyId,
237-
args.expiresAt,
238-
);
249+
try {
250+
return await this.tokenService.generateApiKeyToken(
251+
workspaceId,
252+
args.apiKeyId,
253+
args.expiresAt,
254+
);
255+
} catch (error) {
256+
authGraphqlApiExceptionHandler(error);
257+
}
239258
}
240259

241260
@Mutation(() => EmailPasswordResetLink)
242261
async emailPasswordResetLink(
243262
@Args() emailPasswordResetInput: EmailPasswordResetLinkInput,
244-
): Promise<EmailPasswordResetLink> {
245-
const resetToken = await this.tokenService.generatePasswordResetToken(
246-
emailPasswordResetInput.email,
247-
);
248-
249-
return await this.tokenService.sendEmailPasswordResetLink(
250-
resetToken,
251-
emailPasswordResetInput.email,
252-
);
263+
): Promise<EmailPasswordResetLink | void> {
264+
try {
265+
const resetToken = await this.tokenService.generatePasswordResetToken(
266+
emailPasswordResetInput.email,
267+
);
268+
269+
return await this.tokenService.sendEmailPasswordResetLink(
270+
resetToken,
271+
emailPasswordResetInput.email,
272+
);
273+
} catch (error) {
274+
authGraphqlApiExceptionHandler(error);
275+
}
253276
}
254277

255278
@Mutation(() => InvalidatePassword)
256279
async updatePasswordViaResetToken(
257280
@Args() args: UpdatePasswordViaResetTokenInput,
258-
): Promise<InvalidatePassword> {
259-
const { id } = await this.tokenService.validatePasswordResetToken(
260-
args.passwordResetToken,
261-
);
262-
263-
assert(id, 'User not found', NotFoundException);
264-
265-
const { success } = await this.authService.updatePassword(
266-
id,
267-
args.newPassword,
268-
);
281+
): Promise<InvalidatePassword | void> {
282+
try {
283+
const { id } = await this.tokenService.validatePasswordResetToken(
284+
args.passwordResetToken,
285+
);
269286

270-
assert(success, 'Password update failed', InternalServerErrorException);
287+
await this.authService.updatePassword(id, args.newPassword);
271288

272-
return await this.tokenService.invalidatePasswordResetToken(id);
289+
return await this.tokenService.invalidatePasswordResetToken(id);
290+
} catch (error) {
291+
authGraphqlApiExceptionHandler(error);
292+
}
273293
}
274294

275295
@Query(() => ValidatePasswordResetToken)
276296
async validatePasswordResetToken(
277297
@Args() args: ValidatePasswordResetTokenInput,
278-
): Promise<ValidatePasswordResetToken> {
279-
return this.tokenService.validatePasswordResetToken(
280-
args.passwordResetToken,
281-
);
298+
): Promise<ValidatePasswordResetToken | void> {
299+
try {
300+
return this.tokenService.validatePasswordResetToken(
301+
args.passwordResetToken,
302+
);
303+
} catch (error) {
304+
authGraphqlApiExceptionHandler(error);
305+
}
282306
}
283307
}

packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import {
2-
Controller,
3-
Get,
4-
Req,
5-
Res,
6-
UnauthorizedException,
7-
UseGuards,
8-
} from '@nestjs/common';
1+
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
92

103
import { Response } from 'express';
114

5+
import {
6+
AuthException,
7+
AuthExceptionCode,
8+
} from 'src/engine/core-modules/auth/auth.exception';
129
import { GoogleAPIsOauthExchangeCodeForTokenGuard } from 'src/engine/core-modules/auth/guards/google-apis-oauth-exchange-code-for-token.guard';
1310
import { GoogleAPIsOauthRequestCodeGuard } from 'src/engine/core-modules/auth/guards/google-apis-oauth-request-code.guard';
1411
import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service';
@@ -59,13 +56,17 @@ export class GoogleAPIsAuthController {
5956
const demoWorkspaceIds = this.environmentService.get('DEMO_WORKSPACE_IDS');
6057

6158
if (demoWorkspaceIds.includes(workspaceId)) {
62-
throw new UnauthorizedException(
59+
throw new AuthException(
6360
'Cannot connect Google account to demo workspace',
61+
AuthExceptionCode.FORBIDDEN_EXCEPTION,
6462
);
6563
}
6664

6765
if (!workspaceId) {
68-
throw new Error('Workspace not found');
66+
throw new AuthException(
67+
'Workspace not found',
68+
AuthExceptionCode.INVALID_INPUT,
69+
);
6970
}
7071

7172
const handle = emails[0].value;

packages/twenty-server/src/engine/core-modules/auth/guards/google-apis-oauth-exchange-code-for-token.guard.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import {
2-
ExecutionContext,
3-
Injectable,
4-
NotFoundException,
5-
} from '@nestjs/common';
1+
import { ExecutionContext, Injectable } from '@nestjs/common';
62
import { AuthGuard } from '@nestjs/passport';
73
import { InjectRepository } from '@nestjs/typeorm';
84

95
import { Repository } from 'typeorm';
106

7+
import {
8+
AuthException,
9+
AuthExceptionCode,
10+
} from 'src/engine/core-modules/auth/auth.exception';
1111
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
1212
import {
1313
GoogleAPIScopeConfig,
@@ -39,7 +39,10 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
3939
!this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') &&
4040
!this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED')
4141
) {
42-
throw new NotFoundException('Google apis auth is not enabled');
42+
throw new AuthException(
43+
'Google apis auth is not enabled',
44+
AuthExceptionCode.FORBIDDEN_EXCEPTION,
45+
);
4346
}
4447

4548
const { workspaceId } = await this.tokenService.verifyTransientToken(

0 commit comments

Comments
 (0)