Skip to content

Commit

Permalink
feat: Connect session create & user create to SSO
Browse files Browse the repository at this point in the history
  • Loading branch information
aXenDeveloper committed Nov 24, 2024
1 parent b0280fb commit e99f9e6
Show file tree
Hide file tree
Showing 25 changed files with 859 additions and 188 deletions.
4 changes: 4 additions & 0 deletions apps/frontend/src/plugins/core/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@
"desc": "Not recommended on shared computers."
},
"submit": "Log In",
"sso_first_login": {
"title": "Almost there!",
"desc": "Please complete your registration by entering your name."
},
"error": {
"title": "Invalid credentials",
"desc": "The email address or password was incorrect. Please try again (make sure your caps lock is off)."
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/core/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {

import { VerifyConfirmEmailAuthService } from './services/confirm_email/verify.service';
import { ShowAuthService } from './services/show.service';
import { SignInAuthService } from './services/sign_in.service';
import { SignInAuthService } from './services/sign_in/sign_in.service';
import { SignOutAuthService } from './services/sign_out.service';
import { SignUpAuthService } from './services/sign_up/sign_up.service';

Expand Down
21 changes: 17 additions & 4 deletions packages/backend/src/core/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,40 @@ import { AuthCron } from './auth.cron';
import { ClearTokenConfirmEmailAuthCron } from './services/confirm_email/clear_tokens_email.cron';
import { VerifyConfirmEmailAuthService } from './services/confirm_email/verify.service';
import { ShowAuthService } from './services/show.service';
import { SignInAuthService } from './services/sign_in.service';
import { HelperSignInAuthService } from './services/sign_in/helper.service';
import { SignInAuthService } from './services/sign_in/sign_in.service';
import { SignOutAuthService } from './services/sign_out.service';
import { HelperSignUpAuthService } from './services/sign_up/helper.service';
import { SendConfirmEmailAuthService } from './services/sign_up/send.confirm_email.service';
import { SignUpAuthService } from './services/sign_up/sign_up.service';
import { SettingsAuthModule } from './settings/settings.module';
import { SSOAuthModule } from './sso/sso.module';

@Module({
providers: [
HelperSignInAuthService,
SendConfirmEmailAuthService,
HelperSignUpAuthService,
],
exports: [
HelperSignInAuthService,
SendConfirmEmailAuthService,
HelperSignUpAuthService,
],
})
export class HelpersAuthModule {}

@Module({
providers: [
ShowAuthService,
SignUpAuthService,
HelperSignUpAuthService,
AuthCron,
ClearTokenConfirmEmailAuthCron,
SendConfirmEmailAuthService,
VerifyConfirmEmailAuthService,
SignInAuthService,
SignOutAuthService,
],
controllers: [AuthController],
imports: [SettingsAuthModule, SSOAuthModule],
imports: [SettingsAuthModule, SSOAuthModule, HelpersAuthModule],
})
export class AuthModule {}
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { core_admin_sessions } from '@/database/schema/admins';
import { core_sessions } from '@/database/schema/sessions';
import { DeviceAuthService } from '@/helpers/auth/device.service';
import { getConfigFile } from '@/helpers/config';

Check warning on line 4 in packages/backend/src/core/auth/services/sign_in/helper.service.ts

View workflow job for this annotation

GitHub Actions / linting

'getConfigFile' is defined but never used
import { EmailHelperService } from '@/helpers/email/email.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ForbiddenException, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { and, eq } from 'drizzle-orm';
import { Request, Response } from 'express';
import { SignInAuthBody, SignInAuthObj } from 'vitnode-shared/auth/auth.dto';
import { SignInAuthBody } from 'vitnode-shared/auth/auth.dto';

import { DeviceAuthService } from '../../../helpers/auth/device.service';
import { verifyPassword } from '../helpers/password';
import { SendConfirmEmailAuthService } from './sign_up/send.confirm_email.service';
import { SendConfirmEmailAuthService } from '../sign_up/send.confirm_email.service';

@Injectable()
export class SignInAuthService {
export class HelperSignInAuthService {
constructor(
private readonly databaseService: InternalDatabaseService,
private readonly jwtService: JwtService,
Expand All @@ -25,7 +24,7 @@ export class SignInAuthService {
private readonly mailService: EmailHelperService,
) {}

private async createSession({
async createSession({
req,
res,
body: { email, remember, admin, user_id, name },
Expand All @@ -39,7 +38,7 @@ export class SignInAuthService {
const devMode: boolean = this.configService.get('dev_mode') ?? false;
const device = await this.deviceService.getDevice({ req, res });
if (device.uagent_os === 'Uagent from tests' && !devMode) {
throw new HttpException('ACCESS_DENIED', HttpStatus.UNAUTHORIZED);
throw new ForbiddenException('ACCESS_DENIED');
}

const login_token = this.jwtService.sign(
Expand Down Expand Up @@ -192,82 +191,4 @@ export class SignInAuthService {

return login_token;
}

async singIn({
req,
res,
body: { admin, email: emailRaw, password, ...rest },
}: {
body: SignInAuthBody;
req: Request;
res: Response;
}): Promise<SignInAuthObj> {
const { settings } = getConfigFile();
const email = emailRaw.toLowerCase();
const user = await this.databaseService.db.query.core_users.findFirst({
where: (table, { eq }) => eq(table.email, email),
with: {
confirm_email: true,
},
columns: {
id: true,
email_verified: true,
group_id: true,
name: true,
password: true,
language: true,
name_seo: true,
avatar_color: true,
},
});
if (!user?.password) {
throw new HttpException('ACCESS_DENIED', HttpStatus.UNAUTHORIZED);
}

const validPassword = await verifyPassword(password, user.password);
if (!validPassword) {
throw new HttpException('ACCESS_DENIED', HttpStatus.UNAUTHORIZED);
}

if (
!user.email_verified &&
settings.authorization.require_confirm_email &&
this.mailService.checkIfEnable()
) {
await this.sendConfirmEmailCoreSessionsService.sendConfirmEmail({
userId: user.id,
});

throw new HttpException('EMAIL_NOT_VERIFIED', HttpStatus.UNAUTHORIZED);
}

// If admin mode is enabled, check if user has access to admin cp
if (admin) {
const accessToAdminCP =
await this.databaseService.db.query.core_admin_permissions.findFirst({
where: (table, { eq, or }) =>
or(
user.group_id ? eq(table.group_id, user.group_id) : undefined,
eq(table.user_id, user.id),
),
});
if (!accessToAdminCP) {
throw new HttpException('ACCESS_DENIED', HttpStatus.UNAUTHORIZED);
}
}

const loginToken = await this.createSession({
req,
res,
body: { admin, email, user_id: user.id, name: user.name, ...rest },
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: _, ...userWithoutPassword } = user;

return {
login_token: loginToken,
...userWithoutPassword,
};
}
}
103 changes: 103 additions & 0 deletions packages/backend/src/core/auth/services/sign_in/sign_in.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { getConfigFile } from '@/helpers/config';
import { EmailHelperService } from '@/helpers/email/email.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import {
ForbiddenException,
HttpException,
HttpStatus,
Injectable,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { SignInAuthBody, SignInAuthObj } from 'vitnode-shared/auth/auth.dto';

import { verifyPassword } from '../../helpers/password';
import { SendConfirmEmailAuthService } from '../sign_up/send.confirm_email.service';
import { HelperSignInAuthService } from './helper.service';

@Injectable()
export class SignInAuthService {
constructor(
private readonly databaseService: InternalDatabaseService,
private readonly signInHelper: HelperSignInAuthService,
private readonly sendConfirmEmailCoreSessionsService: SendConfirmEmailAuthService,
private readonly mailService: EmailHelperService,
) {}

async singIn({
req,
res,
body: { admin, email: emailRaw, password, ...rest },
}: {
body: SignInAuthBody;
req: Request;
res: Response;
}): Promise<SignInAuthObj> {
const { settings } = getConfigFile();
const email = emailRaw.toLowerCase();
const user = await this.databaseService.db.query.core_users.findFirst({
where: (table, { eq }) => eq(table.email, email),
with: {
confirm_email: true,
},
columns: {
id: true,
email_verified: true,
group_id: true,
name: true,
password: true,
language: true,
name_seo: true,
avatar_color: true,
},
});
if (!user?.password) {
throw new ForbiddenException('ACCESS_DENIED');
}

const validPassword = await verifyPassword(password, user.password);
if (!validPassword) {
throw new ForbiddenException('ACCESS_DENIED');
}

if (
!user.email_verified &&
settings.authorization.require_confirm_email &&
this.mailService.checkIfEnable()
) {
await this.sendConfirmEmailCoreSessionsService.sendConfirmEmail({
userId: user.id,
});

throw new HttpException('EMAIL_NOT_VERIFIED', HttpStatus.UNAUTHORIZED);
}

// If admin mode is enabled, check if user has access to admin cp
if (admin) {
const accessToAdminCP =
await this.databaseService.db.query.core_admin_permissions.findFirst({
where: (table, { eq, or }) =>
or(
user.group_id ? eq(table.group_id, user.group_id) : undefined,
eq(table.user_id, user.id),
),
});
if (!accessToAdminCP) {
throw new ForbiddenException('ACCESS_DENIED');
}
}

const loginToken = await this.signInHelper.createSession({
req,
res,
body: { admin, email, user_id: user.id, name: user.name, ...rest },
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: _, ...userWithoutPassword } = user;

return {
login_token: loginToken,
...userWithoutPassword,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class HelperSignUpAuthService {
private readonly configService: ConfigService,
) {}

private readonly getDefaultData = async (): Promise<{
readonly getDefaultData = async (): Promise<{
email_verified: boolean;
group_id: number;
}> => {
Expand Down Expand Up @@ -62,7 +62,7 @@ export class HelperSignUpAuthService {
};
};

private readonly getLanguage = async (req: Request): Promise<string> => {
readonly getLanguage = async (req: Request): Promise<string> => {
const languageToSet: string =
req.cookies[this.configService.get('cookies.lang') ?? 'NEXT_LOCALE'];

Expand Down Expand Up @@ -102,7 +102,7 @@ export class HelperSignUpAuthService {
const convertToNameSEO = removeSpecialCharacters(name);
const checkNameSEO =
await this.databaseService.db.query.core_users.findFirst({
where: (table, { ilike }) => ilike(table.name_seo, convertToNameSEO),
where: (table, { eq }) => eq(table.name_seo, convertToNameSEO),
});

if (checkNameSEO) {
Expand Down
Loading

0 comments on commit e99f9e6

Please sign in to comment.