diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx index e650104f0..6530532fa 100644 --- a/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx +++ b/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx @@ -6,10 +6,16 @@ import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState'; import { AppPath } from '@/types/AppPath'; import { useSetRecoilState } from 'recoil'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; +import { isDefined } from 'twenty-ui'; export const VerifyEffect = () => { const [searchParams] = useSearchParams(); const loginToken = searchParams.get('loginToken'); + const errorMessage = searchParams.get('errorMessage'); + + const { enqueueSnackBar } = useSnackBar(); const isLogged = useIsLogged(); const navigate = useNavigate(); @@ -22,6 +28,11 @@ export const VerifyEffect = () => { useEffect(() => { const getTokens = async () => { + if (isDefined(errorMessage)) { + enqueueSnackBar(errorMessage, { + variant: SnackBarVariant.Error, + }); + } if (!loginToken) { navigate(AppPath.SignInUp); } else { diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts index 58bd8f2ea..e8518e9fd 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts @@ -109,7 +109,9 @@ export class GoogleAuthController { if (err instanceof AuthException) { return res.redirect( this.domainManagerService.computeRedirectErrorUrl({ - subdomain: this.environmentService.get('DEFAULT_SUBDOMAIN'), + subdomain: + req.user.targetWorkspaceSubdomain ?? + this.environmentService.get('DEFAULT_SUBDOMAIN'), errorMessage: err.message, }), ); diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts index cc8c2af81..cb7f7cb28 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts @@ -19,6 +19,7 @@ import { Workspace, WorkspaceActivationStatus, } from 'src/engine/core-modules/workspace/workspace.entity'; +import { UserService } from 'src/engine/core-modules/user/services/user.service'; jest.mock('bcrypt'); @@ -34,6 +35,7 @@ const EnvironmentServiceGetMock = jest.fn(); const WorkspaceCountMock = jest.fn(); const WorkspaceCreateMock = jest.fn(); const WorkspaceSaveMock = jest.fn(); +const WorkspaceFindOneMock = jest.fn(); describe('SignInUpService', () => { let service: SignInUpService; @@ -56,6 +58,7 @@ describe('SignInUpService', () => { count: WorkspaceCountMock, create: WorkspaceCreateMock, save: WorkspaceSaveMock, + findOne: WorkspaceFindOneMock, }, }, { @@ -119,6 +122,12 @@ describe('SignInUpService', () => { generateSubdomain: jest.fn().mockReturnValue('testSubDomain'), }, }, + { + provide: UserService, + useValue: { + saveDefaultWorkspaceIfUserHasAccessOrThrow: jest.fn(), + }, + }, ], }).compile(); @@ -170,6 +179,9 @@ describe('SignInUpService', () => { workspaceInvitationFindInvitationByWorkspaceSubdomainAndUserEmailMock.mockReturnValueOnce( undefined, ); + WorkspaceFindOneMock.mockReturnValueOnce({ + id: 'another-workspace', + }); const result = await service.signInUp({ email, @@ -374,6 +386,10 @@ describe('SignInUpService', () => { EnvironmentServiceGetMock.mockReturnValueOnce(false); + WorkspaceFindOneMock.mockReturnValueOnce({ + id: 'another-workspace', + }); + (bcrypt.compare as jest.Mock).mockReturnValueOnce(true); await service.signInUp({ diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts index 8ca5c2329..f11b02d7d 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts @@ -33,6 +33,7 @@ import { import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; import { getImageBufferFromUrl } from 'src/utils/image'; import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type'; +import { UserService } from 'src/engine/core-modules/user/services/user.service'; export type SignInUpServiceInput = { email: string; @@ -62,6 +63,7 @@ export class SignInUpService { private readonly httpService: HttpService, private readonly environmentService: EnvironmentService, private readonly domainManagerService: DomainManagerService, + private readonly userService: UserService, ) {} async signInUp({ @@ -177,6 +179,26 @@ export class SignInUpService { }); } + if (targetWorkspaceSubdomain) { + const workspace = await this.workspaceRepository.findOne({ + where: { subdomain: targetWorkspaceSubdomain }, + select: ['id'], + }); + + workspaceValidator.assertIsExist( + workspace, + new AuthException( + 'Workspace not found', + AuthExceptionCode.FORBIDDEN_EXCEPTION, + ), + ); + + await this.userService.saveDefaultWorkspaceIfUserHasAccessOrThrow( + existingUser.id, + workspace.id, + ); + } + return existingUser; }