Files
twenty/packages/twenty-server/src/core/auth/auth.resolver.ts
T
martmull 7425223f83 3628 timebox separate user creation from workspace creation (#3737)
* Remove workspace schema creation from signUp

* Set user workspaceMember nullable

* Remove workspace creation

* Handle null workspace in tokens

* Update onboarding status

* Generate types

* Move createWorkspace to workspace resolver

* Create workspace after signup

* Update createWorkspace return type

* Update createWorkspace return type

* Create core.workspace at signup

* WIP

* Fix create workspace

* Fix create workspace

* Clean code

* Remove useless recoil set

* Simplify create workspace request

* Set currentWorkspace at login

* Fix tests

* Create a recoil value for is workspaceSchema created

* Rename createWorkspace to createWorkspaceSchema

* Code review returns

* Use AppPath when possible

* Try without state

* Fix

* Fixes

* Rename createWorkspaceSchema to activateWorkspace

* Remove defaultAvatarUrl from user

* Add defaultAvatarUrl to core user

This reverts commit 1701c30eb18804558293cc42043aedf96ea888df.

* Add defaultAvatarUrl to core user

This reverts commit 1701c30eb18804558293cc42043aedf96ea888df.

* Fix ci

* Fix tests

* Fix storybook

* Fix test

* Remove useless query

* Fix test

* Fix test

* Fix mock data

* Fix test

* Clean Mock Requests

* Fix tentative

* Revert "Clean Mock Requests"

This reverts commit 8aa20a34363ffddfdee24f18fc80b27ea0ad5e1d.

* Fix

* Revert "Fix"

This reverts commit 2df7e9b6569b8bfb53f6a45391db725e28d16a18.

* Revert "Revert "Clean Mock Requests""

This reverts commit 3aefef8e9600d161434a047e845563d1b8e0692e.

* Revert "Fix tentative"

This reverts commit 13e7748d6f3b3858d30fb08adbc8ad347c5556ee.

* Update filename

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2024-02-09 12:06:11 +01:00

208 lines
6.8 KiB
TypeScript

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import {
BadRequestException,
ForbiddenException,
InternalServerErrorException,
NotFoundException,
UseGuards,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
import { AuthUser } from 'src/decorators/auth/auth-user.decorator';
import { assert } from 'src/utils/assert';
import { Workspace } from 'src/core/workspace/workspace.entity';
import { AuthWorkspace } from 'src/decorators/auth/auth-workspace.decorator';
import { User } from 'src/core/user/user.entity';
import { ApiKeyTokenInput } from 'src/core/auth/dto/api-key-token.input';
import { ValidatePasswordResetToken } from 'src/core/auth/dto/validate-password-reset-token.entity';
import { TransientToken } from 'src/core/auth/dto/transient-token.entity';
import { UserService } from 'src/core/user/services/user.service';
import { ValidatePasswordResetTokenInput } from 'src/core/auth/dto/validate-password-reset-token.input';
import { UpdatePasswordViaResetTokenInput } from 'src/core/auth/dto/update-password-via-reset-token.input';
import { EmailPasswordResetLink } from 'src/core/auth/dto/email-password-reset-link.entity';
import { InvalidatePassword } from 'src/core/auth/dto/invalidate-password.entity';
import { ApiKeyToken, AuthTokens } from './dto/token.entity';
import { TokenService } from './services/token.service';
import { RefreshTokenInput } from './dto/refresh-token.input';
import { Verify } from './dto/verify.entity';
import { VerifyInput } from './dto/verify.input';
import { AuthService } from './services/auth.service';
import { LoginToken } from './dto/login-token.entity';
import { ChallengeInput } from './dto/challenge.input';
import { UserExists } from './dto/user-exists.entity';
import { CheckUserExistsInput } from './dto/user-exists.input';
import { WorkspaceInviteHashValid } from './dto/workspace-invite-hash-valid.entity';
import { WorkspaceInviteHashValidInput } from './dto/workspace-invite-hash.input';
import { SignUpInput } from './dto/sign-up.input';
import { ImpersonateInput } from './dto/impersonate.input';
@Resolver()
export class AuthResolver {
constructor(
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private authService: AuthService,
private tokenService: TokenService,
private userService: UserService,
) {}
@Query(() => UserExists)
async checkUserExists(
@Args() checkUserExistsInput: CheckUserExistsInput,
): Promise<UserExists> {
const { exists } = await this.authService.checkUserExists(
checkUserExistsInput.email,
);
return { exists };
}
@Query(() => WorkspaceInviteHashValid)
async checkWorkspaceInviteHashIsValid(
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
): Promise<WorkspaceInviteHashValid> {
return await this.authService.checkWorkspaceInviteHashIsValid(
workspaceInviteHashValidInput.inviteHash,
);
}
@Query(() => Workspace)
async findWorkspaceFromInviteHash(
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
) {
return await this.workspaceRepository.findOneBy({
inviteHash: workspaceInviteHashValidInput.inviteHash,
});
}
@Mutation(() => LoginToken)
async challenge(@Args() challengeInput: ChallengeInput): Promise<LoginToken> {
const user = await this.authService.challenge(challengeInput);
const loginToken = await this.tokenService.generateLoginToken(user.email);
return { loginToken };
}
@Mutation(() => LoginToken)
async signUp(@Args() signUpInput: SignUpInput): Promise<LoginToken> {
const user = await this.authService.signUp(signUpInput);
const loginToken = await this.tokenService.generateLoginToken(user.email);
return { loginToken };
}
@Mutation(() => TransientToken)
@UseGuards(JwtAuthGuard)
async generateTransientToken(
@AuthUser() user: User,
): Promise<TransientToken | void> {
const workspaceMember = await this.userService.loadWorkspaceMember(user);
if (!workspaceMember) {
return;
}
const transientToken = await this.tokenService.generateTransientToken(
workspaceMember.id,
user.defaultWorkspace.id,
);
return { transientToken };
}
@Mutation(() => Verify)
async verify(@Args() verifyInput: VerifyInput): Promise<Verify> {
const email = await this.tokenService.verifyLoginToken(
verifyInput.loginToken,
);
const result = await this.authService.verify(email);
return result;
}
@Mutation(() => AuthTokens)
async renewToken(@Args() args: RefreshTokenInput): Promise<AuthTokens> {
if (!args.refreshToken) {
throw new BadRequestException('Refresh token is mendatory');
}
const tokens = await this.tokenService.generateTokensFromRefreshToken(
args.refreshToken,
);
return { tokens: tokens };
}
@UseGuards(JwtAuthGuard)
@Mutation(() => Verify)
async impersonate(
@Args() impersonateInput: ImpersonateInput,
@AuthUser() user: User,
): Promise<Verify> {
// Check if user can impersonate
assert(user.canImpersonate, 'User cannot impersonate', ForbiddenException);
return this.authService.impersonate(impersonateInput.userId);
}
@UseGuards(JwtAuthGuard)
@Mutation(() => ApiKeyToken)
async generateApiKeyToken(
@Args() args: ApiKeyTokenInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
): Promise<ApiKeyToken | undefined> {
return await this.tokenService.generateApiKeyToken(
workspaceId,
args.apiKeyId,
args.expiresAt,
);
}
@UseGuards(JwtAuthGuard)
@Mutation(() => EmailPasswordResetLink)
async emailPasswordResetLink(
@AuthUser() { email }: User,
): Promise<EmailPasswordResetLink> {
const resetToken =
await this.tokenService.generatePasswordResetToken(email);
return await this.tokenService.sendEmailPasswordResetLink(
resetToken,
email,
);
}
@Mutation(() => InvalidatePassword)
async updatePasswordViaResetToken(
@Args() args: UpdatePasswordViaResetTokenInput,
): Promise<InvalidatePassword> {
const { id } = await this.tokenService.validatePasswordResetToken(
args.passwordResetToken,
);
assert(id, 'User not found', NotFoundException);
const { success } = await this.authService.updatePassword(
id,
args.newPassword,
);
assert(success, 'Password update failed', InternalServerErrorException);
return await this.tokenService.invalidatePasswordResetToken(id);
}
@Query(() => ValidatePasswordResetToken)
async validatePasswordResetToken(
@Args() args: ValidatePasswordResetTokenInput,
): Promise<ValidatePasswordResetToken> {
return this.tokenService.validatePasswordResetToken(
args.passwordResetToken,
);
}
}