Skip to content

Commit 3bc7ad0

Browse files
authored
Merge pull request #7667 from ever-co/fix/email-password-authentication
[Fix] Email / Password Authentication
2 parents 2ecb746 + 61e4486 commit 3bc7ad0

File tree

2 files changed

+149
-90
lines changed

2 files changed

+149
-90
lines changed

packages/core/src/auth/auth.controller.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { ApiTags, ApiOperation, ApiResponse, ApiOkResponse, ApiBadRequestRespons
1616
import { CommandBus } from '@nestjs/cqrs';
1717
import { I18nLang } from 'nestjs-i18n';
1818
import { IAuthResponse, IUserSigninWorkspaceResponse, LanguagesEnum } from '@gauzy/contracts';
19-
import { Public, parseToBoolean } from '@gauzy/common';
19+
import { Public } from '@gauzy/common';
2020
import { AuthService } from './auth.service';
2121
import { User as IUser } from '../user/user.entity';
2222
import {
@@ -26,6 +26,7 @@ import {
2626
WorkspaceSigninVerifyTokenCommand
2727
} from './commands';
2828
import { RequestContext } from '../core/context';
29+
import { convertNativeParameters } from '../core/crud/pagination.helper';
2930
import { AuthRefreshGuard } from './../shared/guards';
3031
import { ChangePasswordRequestDTO, ResetPasswordRequestDTO } from './../password-reset/dto';
3132
import { RegisterUserDTO, UserEmailDTO, UserLoginDTO, UserSigninWorkspaceDTO } from './../user/dto';
@@ -138,8 +139,12 @@ export class AuthController {
138139
@Post('/login')
139140
@Public()
140141
@UsePipes(new ValidationPipe({ transform: true }))
141-
async login(@Body() input: UserLoginDTO): Promise<IAuthResponse | null> {
142-
return await this.commandBus.execute(new AuthLoginCommand(input));
142+
async login(
143+
@Body() input: UserLoginDTO
144+
): Promise<IAuthResponse | null> {
145+
return await this.commandBus.execute(
146+
new AuthLoginCommand(input)
147+
);
143148
}
144149

145150
/**
@@ -152,8 +157,14 @@ export class AuthController {
152157
@Post('/signin.email.password')
153158
@Public()
154159
@UsePipes(new ValidationPipe())
155-
async signinWorkspacesByPassword(@Body() input: UserSigninWorkspaceDTO): Promise<IUserSigninWorkspaceResponse> {
156-
return await this.authService.signinWorkspacesByEmailPassword(input);
160+
async signinWorkspacesByPassword(
161+
@Query() query: Record<string, boolean>,
162+
@Body() input: UserSigninWorkspaceDTO
163+
): Promise<IUserSigninWorkspaceResponse> {
164+
return await this.authService.signinWorkspacesByEmailPassword(
165+
input,
166+
convertNativeParameters(query.includeTeams)
167+
);
157168
}
158169

159170
/**
@@ -191,7 +202,7 @@ export class AuthController {
191202
): Promise<IUserSigninWorkspaceResponse> {
192203
return await this.authService.confirmWorkspaceSigninByCode(
193204
input,
194-
parseToBoolean(query.includeTeams)
205+
convertNativeParameters(query.includeTeams)
195206
);
196207
}
197208

packages/core/src/auth/auth.service.ts

Lines changed: 132 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,27 @@ export class AuthService extends SocialAuthService {
7474
async login({ email, password }: IUserLoginInput): Promise<IAuthResponse | null> {
7575
try {
7676
const user = await this.userService.findOneByOptions({
77-
where: {
78-
email,
79-
isActive: true,
80-
isArchived: false
81-
},
77+
where: [
78+
{
79+
email,
80+
isActive: true,
81+
isArchived: false,
82+
hash: Not(IsNull()),
83+
employee: {
84+
id: IsNull()
85+
}
86+
},
87+
{
88+
email,
89+
isActive: true,
90+
isArchived: false,
91+
hash: Not(IsNull()),
92+
employee: {
93+
isActive: true, // If employees are inactive
94+
isArchived: false
95+
}
96+
}
97+
],
8298
relations: {
8399
employee: true,
84100
role: true
@@ -87,10 +103,7 @@ export class AuthService extends SocialAuthService {
87103
createdAt: 'DESC'
88104
}
89105
});
90-
// If employees are inactive
91-
if (isNotEmpty(user.employee) && user.employee.isActive === false) {
92-
throw new UnauthorizedException();
93-
}
106+
94107
// If password is not matching with any user
95108
if (!(await bcrypt.compare(password, user.hash))) {
96109
throw new UnauthorizedException();
@@ -120,10 +133,11 @@ export class AuthService extends SocialAuthService {
120133
* @returns A promise that resolves to a response with user workspaces.
121134
* @throws UnauthorizedException if authentication fails.
122135
*/
123-
async signinWorkspacesByEmailPassword({
124-
email,
125-
password
126-
}: IUserWorkspaceSigninInput): Promise<IUserSigninWorkspaceResponse> {
136+
async signinWorkspacesByEmailPassword(
137+
input: IUserWorkspaceSigninInput,
138+
includeTeams: boolean
139+
): Promise<IUserSigninWorkspaceResponse> {
140+
const { email, password } = input;
127141
/** Fetching users matching the query */
128142
let users = await this.userService.find({
129143
where: [
@@ -178,31 +192,15 @@ export class AuthService extends SocialAuthService {
178192
codeExpireAt
179193
});
180194

181-
// Create an array of user objects with relevant data
182-
const workspaces: IWorkspaceResponse[] = users.map((user: IUser) => ({
183-
user: new User({
184-
id: user.id,
185-
email: user.email || null,
186-
name: user.name,
187-
imageUrl: user.imageUrl,
188-
tenant: new Tenant({
189-
id: user.tenant ? user.tenantId : null,
190-
name: user.tenant?.name || '',
191-
logo: user.tenant?.logo || ''
192-
})
193-
}),
194-
token: this.generateToken(user, code)
195-
}));
196-
197195
// Determining the response based on the number of matching users
198-
const response: IUserSigninWorkspaceResponse = {
199-
workspaces: workspaces,
200-
confirmed_email: email,
201-
show_popup: workspaces.length > 1,
202-
total_workspaces: workspaces.length
203-
};
196+
const response: IUserSigninWorkspaceResponse = await this.createUserSigninWorkspaceResponse({
197+
users,
198+
code,
199+
email,
200+
includeTeams
201+
});
204202

205-
if (workspaces.length > 0) {
203+
if (response.total_workspaces > 0) {
206204
return response;
207205
} else {
208206
console.log('Error while signin workspace: %s');
@@ -714,7 +712,7 @@ export class AuthService extends SocialAuthService {
714712

715713
// Check the value of the 'email' variable against certain demo email addresses
716714
if (email === demoEmployeeEmail || email === demoAdminEmail) {
717-
magicCode = environment.demoCredentialConfig?.employeePassword || '123456';
715+
magicCode = environment.demoCredentialConfig?.employeePassword || DEMO_PASSWORD_LESS_MAGIC_CODE;
718716
isDemoCode = true;
719717
}
720718
}
@@ -803,56 +801,16 @@ export class AuthService extends SocialAuthService {
803801
}
804802
});
805803

806-
// Create an array of user objects with relevant data
807-
const workspaces: IWorkspaceResponse[] = [];
808-
809-
// Create an array of user objects with relevant data
810-
for await (const user of users) {
811-
const userId = user.id;
812-
const tenantId = user.tenant ? user.tenantId : null;
813-
const employeeId = user.employee ? user.employeeId : null;
814-
815-
const workspace: IWorkspaceResponse = {
816-
user: new User({
817-
id: user.id,
818-
email: user.email || null,
819-
name: user.name || null,
820-
imageUrl: user.imageUrl || null,
821-
tenant: new Tenant({
822-
id: user.tenant ? user.tenantId : null,
823-
name: user.tenant?.name || null,
824-
logo: user.tenant?.logo || null
825-
})
826-
}),
827-
token: this.generateToken(user, code)
828-
};
829-
830-
try {
831-
if (includeTeams) {
832-
console.time('Get teams for a user within a specific tenant');
833-
834-
const teams = await this.getTeamsForUser(tenantId, userId, employeeId);
835-
workspace['current_teams'] = teams;
836-
837-
console.timeEnd('Get teams for a user within a specific tenant');
838-
}
839-
} catch (error) {
840-
console.log('Error while getting specific teams for specific tenant: %s', error?.message);
841-
}
842-
843-
workspaces.push(workspace);
844-
}
845-
846804
// Determining the response based on the number of matching users
847-
const response: IUserSigninWorkspaceResponse = {
848-
workspaces,
849-
confirmed_email: email,
850-
show_popup: workspaces.length > 1,
851-
total_workspaces: workspaces.length
852-
};
805+
const response: IUserSigninWorkspaceResponse = await this.createUserSigninWorkspaceResponse({
806+
users,
807+
code,
808+
email,
809+
includeTeams
810+
});
853811

854812
// Return the response if there are matching users
855-
if (workspaces.length > 0) {
813+
if (response.total_workspaces > 0) {
856814
return response;
857815
}
858816
throw new UnauthorizedException();
@@ -1030,4 +988,94 @@ export class AuthService extends SocialAuthService {
1030988

1031989
return await query.getRawMany();
1032990
}
991+
992+
/**
993+
* Creates workspace response objects for a list of users.
994+
*
995+
* @param {Object} params - The parameters.
996+
* @param {IUser[]} params.users - The list of users.
997+
* @param {string} params.email - The email address.
998+
* @param {string} params.code - The code for workspace signin.
999+
* @param {boolean} params.includeTeams - Flag to include teams in the response.
1000+
* @returns {Promise<IUserSigninWorkspaceResponse>} A promise that resolves to the workspace response.
1001+
*/
1002+
private async createUserSigninWorkspaceResponse({
1003+
users,
1004+
email,
1005+
code,
1006+
includeTeams
1007+
}: {
1008+
users: IUser[],
1009+
email: string,
1010+
code: string,
1011+
includeTeams: boolean
1012+
}): Promise<IUserSigninWorkspaceResponse> {
1013+
const workspaces: IWorkspaceResponse[] = [];
1014+
1015+
for (const user of users) {
1016+
const workspace = await this.createWorkspace(user, code, includeTeams);
1017+
workspaces.push(workspace);
1018+
}
1019+
1020+
return {
1021+
workspaces,
1022+
confirmed_email: email,
1023+
show_popup: workspaces.length > 1,
1024+
total_workspaces: workspaces.length
1025+
};
1026+
}
1027+
1028+
/**
1029+
* Creates a workspace response object for a given user.
1030+
*
1031+
* @param user The user object of type IUser.
1032+
* @param code The code used for generating the user token.
1033+
* @param includeTeams Flag indicating whether to include team information in the response.
1034+
* @returns A promise that resolves to the workspace response object of type IWorkspaceResponse.
1035+
*/
1036+
private async createWorkspace(user: IUser, code: string, includeTeams: boolean): Promise<IWorkspaceResponse> {
1037+
const tenantId = user.tenant ? user.tenantId : null;
1038+
const employeeId = user.employee ? user.employeeId : null;
1039+
1040+
const workspace: IWorkspaceResponse = {
1041+
user: this.createUserObject(user),
1042+
token: this.generateToken(user, code)
1043+
};
1044+
1045+
if (includeTeams) {
1046+
try {
1047+
console.time('Get teams for a user within a specific tenant');
1048+
1049+
const teams = await this.getTeamsForUser(tenantId, user.id, employeeId);
1050+
workspace['current_teams'] = teams;
1051+
1052+
console.timeEnd('Get teams for a user within a specific tenant');
1053+
} catch (error) {
1054+
console.error('Error while getting specific teams for specific tenant:', error?.message);
1055+
// Optionally, you might want to handle the error more explicitly here.
1056+
}
1057+
}
1058+
1059+
return workspace;
1060+
}
1061+
1062+
/**
1063+
* Creates a new User object from a given IUser object.
1064+
*
1065+
* @param user The IUser object to be transformed.
1066+
* @returns A new User object with properties mapped from the IUser object.
1067+
*/
1068+
private createUserObject(user: IUser): User {
1069+
return new User({
1070+
id: user.id,
1071+
email: user.email || null, // Sets email to null if it's undefined
1072+
name: user.name || null, // Sets name to null if it's undefined
1073+
imageUrl: user.imageUrl || null, // Sets imageUrl to null if it's undefined
1074+
tenant: user.tenant ? new Tenant({
1075+
id: user.tenant.id, // Assuming tenantId is a direct property of tenant
1076+
name: user.tenant.name || '', // Defaulting to an empty string if name is undefined
1077+
logo: user.tenant.logo || '' // Defaulting to an empty string if logo is undefined
1078+
}) : null // Sets tenant to null if user.tenant is undefined
1079+
});
1080+
}
10331081
}

0 commit comments

Comments
 (0)