Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
♻️ Add origin, session ID, role to auth
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Jan 9, 2021
1 parent 0ebf742 commit 10e7ab8
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 97 deletions.
58 changes: 49 additions & 9 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Body, Controller, Headers, Ip, Post } from '@nestjs/common';
import {
Body,
Controller,
Headers,
HttpCode,
HttpStatus,
Ip,
Post,
} from '@nestjs/common';
import { User } from '@prisma/client';
import { Expose } from '../../providers/prisma/prisma.interface';
import {
Expand All @@ -20,23 +28,28 @@ import { RateLimit } from './rate-limit.decorator';
export class AuthController {
constructor(private authService: AuthService) {}

/** Login to an account */
@Post('login')
@RateLimit(10)
async login(
@Body() data: LoginDto,
@Ip() ip: string,
@Headers('User-Agent') userAgent: string,
@Body('origin') origin?: string,
): Promise<TokenResponse | TotpTokenResponse> {
return this.authService.login(
ip,
userAgent,
data.email,
data.password,
data.code,
origin,
);
}

/** Create a new account */
@Post('register')
@HttpCode(HttpStatus.CREATED)
@RateLimit(10)
async register(
@Ip() ip: string,
Expand All @@ -45,6 +58,7 @@ export class AuthController {
return this.authService.register(ip, data);
}

/** Get a new access token using a refresh token */
@Post('refresh')
@RateLimit(5)
async refresh(
Expand All @@ -55,12 +69,17 @@ export class AuthController {
return this.authService.refresh(ip, userAgent, refreshToken);
}

/** Logout from a session */
@Post('logout')
@RateLimit(5)
async logout(@Body('token') refreshToken: string): Promise<void> {
return this.authService.logout(refreshToken);
async logout(
@Body('token') refreshToken: string,
): Promise<{ success: true }> {
await this.authService.logout(refreshToken);
return { success: true };
}

/** Approve a new subnet */
@Post('approve-subnet')
@RateLimit(5)
async approveSubnet(
Expand All @@ -71,27 +90,38 @@ export class AuthController {
return this.authService.approveSubnet(ip, userAgent, token);
}

/** Resend email verification link */
@Post('resend-email-verification')
@RateLimit(10)
async resendVerify(@Body() data: ResendEmailVerificationDto) {
return this.authService.sendEmailVerification(data.email, true);
async resendVerify(
@Body() data: ResendEmailVerificationDto,
@Body('origin') origin?: string,
) {
return this.authService.sendEmailVerification(data.email, true, origin);
}

/** Verify a new email */
@Post('verify-email')
async verifyEmail(
@Ip() ip: string,
@Headers('User-Agent') userAgent: string,
@Body() data: VerifyEmailDto,
@Body('origin') origin?: string,
): Promise<TokenResponse> {
return this.authService.verifyEmail(ip, userAgent, data.token);
return this.authService.verifyEmail(ip, userAgent, data.token, origin);
}

/** Send a password reset link */
@Post('forgot-password')
@RateLimit(10)
async forgotPassword(@Body() data: ForgotPasswordDto) {
return this.authService.requestPasswordReset(data.email);
async forgotPassword(
@Body() data: ForgotPasswordDto,
@Body('origin') origin?: string,
) {
return this.authService.requestPasswordReset(data.email, origin);
}

/** Reset password */
@Post('reset-password')
@RateLimit(10)
async resetPassword(
Expand All @@ -108,16 +138,25 @@ export class AuthController {
);
}

/** Login using TOTP */
@Post('login/totp')
@RateLimit(10)
async totpLogin(
@Body() data: TotpLoginDto,
@Ip() ip: string,
@Headers('User-Agent') userAgent: string,
@Body('origin') origin?: string,
): Promise<TokenResponse> {
return this.authService.loginWithTotp(ip, userAgent, data.token, data.code);
return this.authService.loginWithTotp(
ip,
userAgent,
data.token,
data.code,
origin,
);
}

/** Login using a token */
@Post('login/token')
@RateLimit(10)
async emailTokenLoginPost(
Expand All @@ -128,6 +167,7 @@ export class AuthController {
return this.authService.loginWithEmailToken(ip, userAgent, token);
}

/** Merge two user accounts */
@Post('merge-accounts')
@RateLimit(10)
async merge(@Body('token') token: string): Promise<{ success: true }> {
Expand Down
25 changes: 24 additions & 1 deletion src/modules/auth/auth.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
IsIn,
IsLocale,
IsNotEmpty,
IsObject,
IsOptional,
IsString,
IsUrl,
Expand All @@ -18,6 +17,10 @@ export class RegisterDto {
@MinLength(3)
name!: string;

@IsString()
@IsOptional()
origin?: string;

@IsEmail()
@IsNotEmpty()
email!: string;
Expand Down Expand Up @@ -75,12 +78,20 @@ export class ResendEmailVerificationDto {
@IsEmail()
@IsNotEmpty()
email!: string;

@IsString()
@IsOptional()
origin?: string;
}

export class ForgotPasswordDto {
@IsEmail()
@IsNotEmpty()
email!: string;

@IsString()
@IsOptional()
origin?: string;
}

export class ResetPasswordDto {
Expand Down Expand Up @@ -108,6 +119,10 @@ export class LoginDto {
@IsOptional()
password?: string;

@IsString()
@IsOptional()
origin?: string;

@IsString()
@Length(6)
@IsOptional()
Expand All @@ -119,6 +134,10 @@ export class TotpLoginDto {
@IsNotEmpty()
token!: string;

@IsString()
@IsOptional()
origin?: string;

@IsString()
@Length(6)
@IsNotEmpty()
Expand All @@ -129,4 +148,8 @@ export class VerifyEmailDto {
@IsString()
@IsNotEmpty()
token!: string;

@IsString()
@IsOptional()
origin?: string;
}
7 changes: 6 additions & 1 deletion src/modules/auth/auth.interface.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { Request as NestRequest } from '@nestjs/common';
import { UserRole } from '@prisma/client';
import type { Request as ExpressRequest } from 'express';

export type MfaMethod = 'NONE' | 'SMS' | 'TOTP' | 'EMAIL';

export interface AccessTokenClaims {
sub: string;
id: number;
scopes: string[];
sessionId: number;
role?: UserRole;
}

export interface TokenResponse {
Expand All @@ -23,6 +26,8 @@ export interface AccessTokenParsed {
id: number;
scopes: string[];
type: 'user' | 'api-key';
sessionId?: number;
role?: UserRole;
}

export interface MfaTokenPayload {
Expand Down
Loading

0 comments on commit 10e7ab8

Please sign in to comment.