mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 19:41:53 +03:00
feat(auth): enhance workspace handling and error feedback (#9118)
Add support for setting a user's default workspace during sign-in if a target workspace subdomain exists. Enhance error feedback by displaying authentication error messages using a Snackbar in the front-end and improving redirect logic for workspace-specific errors.
This commit is contained in:
parent
01fc70da0f
commit
550756c2bf
@ -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 {
|
||||
|
@ -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,
|
||||
}),
|
||||
);
|
||||
|
@ -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({
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user