File

src/auth/auth.service.ts

Index

Properties
Methods

Constructor

constructor(configService: ConfigService, utilService: UtilsService, usersService: UsersService, httpService: HttpService, databaseService: DatabaseService, codesService: CodesService, emailService: EmailsService, discordService: DiscordService, githubService: GithubService)
Parameters :
Name Type Optional
configService ConfigService No
utilService UtilsService No
usersService UsersService No
httpService HttpService No
databaseService DatabaseService No
codesService CodesService No
emailService EmailsService No
discordService DiscordService No
githubService GithubService No

Methods

Public createJwt
createJwt(payload: string | Object)
Parameters :
Name Type Optional
payload string | Object No
Returns : string
Public decodeJwt
decodeJwt(jwt?: string)
Parameters :
Name Type Optional
jwt string Yes
Returns : string | undefined
Public Async forceLogUserIn
forceLogUserIn(user: string)
Parameters :
Name Type Optional
user string No
Public Async get2faStatus
get2faStatus(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : Promise<boolean>
Public Async getEmailAndUserValidity
getEmailAndUserValidity(snowflake: string | undefined)
Parameters :
Name Type Optional
snowflake string | undefined No
Returns : unknown
Private Async getOtpToken
getOtpToken(user: string)
Parameters :
Name Type Optional
user string No
Returns : Promise<string | undefined>
Public Async handleDiscordOauthPassW
handleDiscordOauthPassW(dto: SetOauthPassDto, userId: string, tok: string, tokType: string)
Parameters :
Name Type Optional
dto SetOauthPassDto No
userId string No
tok string No
tokType string No
Returns : unknown
Public Async handleGithubOauthPassW
handleGithubOauthPassW(dto: SetOauthPassDto, userId: string, tok: string)
Parameters :
Name Type Optional
dto SetOauthPassDto No
userId string No
tok string No
Returns : unknown
Public Async initiallyCreate2faToken
initiallyCreate2faToken(user: string)
Parameters :
Name Type Optional
user string No
Returns : Promise<Init2faRetVal>
Public Async initResetPass
initResetPass(dto: InitResetPassDto)
Parameters :
Name Type Optional
dto InitResetPassDto No
Returns : unknown
Public Async is2faCodeValid
is2faCodeValid(user: string, token: string)
Parameters :
Name Type Optional
user string No
token string No
Returns : Promise<boolean>
Public Async isPasswordCorrectForUser
isPasswordCorrectForUser(user: string, password: string)
Parameters :
Name Type Optional
user string No
password string No
Returns : Promise<boolean>
Public Async resetPass
resetPass(dto: ResetPassDto)
Parameters :
Name Type Optional
dto ResetPassDto No
Returns : unknown
Public Async toggle2fa
toggle2fa(user: string, state: boolean)
Parameters :
Name Type Optional
user string No
state boolean No
Returns : Promise<void>
Private Async update2faToken
update2faToken(user: string, token: string, codes: string[])
Parameters :
Name Type Optional
user string No
token string No
codes string[] No
Returns : any
Public Async validateUser
validateUser(dto: LoginDto, agent: string)
Parameters :
Name Type Optional
dto LoginDto No
agent string No
Public Async verifyCaptcha
verifyCaptcha(token: string, ip?: string)
Parameters :
Name Type Optional
token string No
ip string Yes
Returns : Promise<boolean>

Properties

Private Readonly logger
Default value : new Logger(AuthService.name)
import {
  forwardRef,
  Inject,
  Injectable,
  Logger,
  UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as jaywee from 'jsonwebtoken';
import LoginDto from './dto/login.dto';
import { UtilsService } from '../utils/utils.service';
import { UsersService } from '../users/users.service';
import { LoginEnum } from '../utils/enums/responses/login.enum';
import { HttpService } from '@nestjs/axios';
import { GenericResponse, Init2faRetVal } from '../../types';
import { GenericException } from '../exceptions/generic.exception';
import { InvalidUserException } from '../exceptions/invalid.exception';
import * as speakeasy from 'speakeasy';
import { DatabaseService } from '../database/database.service';
import { CodesService } from '../codes/codes.service';
import { InitResetPassDto, ResetPassDto } from './dto/resetpass.dto';
import { EmailsService } from '../emails/emails.service';
import { GenericRespEnum } from '../utils/enums/responses.enum';
import { SetOauthPassDto } from './dto/setpass.dto';
import { DiscordService } from './discord/discord.service';
import { GithubService } from './github/github.service';

@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name);
  constructor(
    private readonly configService: ConfigService,
    private readonly utilService: UtilsService,
    private readonly usersService: UsersService,
    private readonly httpService: HttpService,
    private readonly databaseService: DatabaseService,
    private readonly codesService: CodesService,
    private readonly emailService: EmailsService,
    private readonly discordService: DiscordService,
    private readonly githubService: GithubService,
  ) {}
  public createJwt(payload: string | Object): string {
    return jaywee.sign(
      payload,
      this.configService.get<string>('JWT_SECRET') as string,
      {
        algorithm: 'HS256',
      },
    );
  }

  public decodeJwt(jwt?: string): string | undefined {
    try {
      return jaywee.verify(
        jwt || 'oi cunt',
        this.configService.get<string>('JWT_SECRET'),
      ) as string;
    } catch (err) {
      return undefined;
    }
  }

  public async validateUser(
    dto: LoginDto,
    agent: string,
  ): Promise<GenericResponse<LoginEnum, string>> {
    //throw new Error("fuck off u daft cunt")
    const transformed = this.utilService.transformCredential(dto.username);
    dto.username = transformed.value;
    const resolvedUser = await this.usersService.getUser(
      transformed.value,
      transformed.type,
    );
    if (!resolvedUser) {
      throw new GenericException(
        this.utilService.Enums.Responses.LoginEnum.ERR_INVALID_CREDS,
      );
    }
    if (!resolvedUser.emailVerified) {
      throw new InvalidUserException(true);
    }

    const pass = await this.usersService.getPassword(resolvedUser.snowflake);
    if (await this.utilService.comparePass(dto.password, pass)) {
      if (await this.get2faStatus(resolvedUser.snowflake)) {
        const code = await this.codesService.createCode(
          'TWOFACTOR_AUTH',
          10,
          resolvedUser.snowflake,
        );

        return {
          message: 'TWOFACTOR_NEEDED',
          data: code.code,
        };
      } else {
        const jwt = this.createJwt({
          snowflake: resolvedUser.snowflake,
        });
        this.logger.debug('JWT', jwt);
        return {
          message: this.utilService.Enums.Responses.LoginEnum.LOGGED_IN,
          data: jwt,
        };
      }
    } else {
      throw new GenericException(
        this.utilService.Enums.Responses.LoginEnum.ERR_INVALID_CREDS,
      );
    }
  }
  public async forceLogUserIn(
    user: string,
  ): Promise<GenericResponse<LoginEnum, string>> {
    const jwt = this.createJwt({
      snowflake: user,
    });
    this.logger.debug('JWT', jwt);
    return {
      message: this.utilService.Enums.Responses.LoginEnum.LOGGED_IN,
      data: jwt,
    };
  }

  public async verifyCaptcha(token: string, ip?: string): Promise<boolean> {
    const r = await this.httpService.post(
      'https://www.google.com/recaptcha/api/siteverify',
      `secret=${process.env.CAPTCHA}&response=${token}&remoteip=${ip}`,
      {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
        },
      },
    );
    const parsed = await r.toPromise();
    return parsed.data.success;
  }

  public async getEmailAndUserValidity(snowflake: string | undefined) {
    if (!snowflake) {
      return { valid: false, emailVerified: false };
    }
    this.logger.debug(`Snowflake: ${snowflake}`);

    const valid = await this.usersService.isSnowflakeValid(snowflake, {
      email_verified: true,
      admin: true,
    });
    return {
      valid: valid.valid,
      emailVerified: !!valid?.excessResult?.email_verified,
      admin: !!valid.excessResult?.admin,
    };
  }

  private async update2faToken(user: string, token: string, codes: string[]) {
    await this.databaseService.write.users.update({
      where: { snowflake: user },
      data: {
        otp_token: token,
        recovery_codes: {
          push: codes,
        },
      },
    });
  }

  public async initiallyCreate2faToken(user: string): Promise<Init2faRetVal> {
    const secret = speakeasy.generateSecret({
      name: 'Fileglass',
      issuer: 'Fileglass',
    });
    const recovery = await this.utilService.createUniformIds(10, 8);
    await this.update2faToken(user, secret.base32, recovery);
    return { qrcode: secret.otpauth_url!, recovery };
  }

  private async getOtpToken(user: string): Promise<string | undefined> {
    const r = await this.databaseService.read.users.findFirst({
      where: { snowflake: user },
      select: {
        otp_token: true,
      },
    });
    return r?.otp_token || undefined;
  }

  public async is2faCodeValid(user: string, token: string): Promise<boolean> {
    const sec = await this.getOtpToken(user);
    if (!sec) {
      return true;
    }
    return speakeasy.totp.verify({
      secret: sec,
      encoding: 'base32',
      token,
    });
  }

  public async toggle2fa(user: string, state: boolean): Promise<void> {
    await this.databaseService.write.users.update({
      where: { snowflake: user },
      data: {
        twostep: state,
      },
    });
  }

  public async isPasswordCorrectForUser(
    user: string,
    password: string,
  ): Promise<boolean> {
    const currpass = await this.databaseService.read.users.findFirst({
      where: { snowflake: user },
      select: { password: true },
    });
    return await this.utilService.comparePass(password, currpass.password);
  }
  public async get2faStatus(snowflake: string): Promise<boolean> {
    const r = await this.databaseService.read.users.findFirst({
      where: {
        snowflake,
      },
      select: {
        twostep: true,
      },
    });
    return r.twostep;
  }

  public async initResetPass(dto: InitResetPassDto) {
    const user = await this.usersService.getUser(
      this.utilService.sha(dto.email),
      'email_hash',
    );
    if (user?.snowflake) {
      this.logger.debug(
        'Valid user for email',
        dto.email,
        'sending password reset email',
      );
      const username = await this.usersService.getUsername(user.snowflake);
      const code = await this.codesService.createCode(
        'PASSWORD_RESET',
        60,
        user.snowflake,
      );
      const bod = this.emailService.bodies.passResetEmail(
        `https://dash.file.glass/resetpassword?q=${code.code}`,
        username,
      );
      await new this.emailService.Mailer(dto.email, 'Reset your password')
        .append(bod)
        .deliver();
    }
    return GenericRespEnum.PASS_RESET_SENT;
  }

  public async resetPass(dto: ResetPassDto) {
    const code = await this.codesService.fetchCode(dto.code);
    await this.usersService.setFieldData(
      code.user,
      'password',
      await this.utilService.hashPass(dto.password),
    );
    return GenericRespEnum.PASS_UPDATED;
  }

  public async handleDiscordOauthPassW(
    dto: SetOauthPassDto,
    userId: string,
    tok: string,
    tokType: string,
  ) {
    const user = await this.discordService.resolveUserByToken(tok, tokType);
    if (!user) {
      throw new UnauthorizedException();
    }

    this.logger.debug('Resolved Discord user', user);
    const pfpUrl = this.discordService.resolvePfp(user.id, user.avatar);
    this.logger.debug('Pfp url: ', pfpUrl);
    const r = await this.usersService.create(
      user.username,
      dto.password,
      user.email,
      user.id,
      false,
    );
    this.logger.debug('User creation finished, setting pfp');
    await this.usersService.setUserPfp('DISCORD', {
      user: user.id,
      name: pfpUrl,
      mime: undefined,
      file: undefined,
    });

    return r;
  }

  public async handleGithubOauthPassW(
    dto: SetOauthPassDto,
    userId: string,
    tok: string,
  ) {
    const user = await this.githubService.fetchUser(tok);
    if (!user) {
      throw new UnauthorizedException();
    }

    const pfpUrl = user.avatar_url; // i love github api <3
    this.logger.debug('Pfp url: ', pfpUrl);
    const r = await this.usersService.create(
      user.login,
      dto.password,
      await this.githubService.fetchUserMainEmail(tok),
      userId,
      false,
    );

    await this.usersService.setUserPfp('GITHUB', {
      user: user.id.toString(),
      name: pfpUrl,
      mime: undefined,
      file: undefined,
    });
    return r;
  }
}

results matching ""

    No results matching ""