This commit is contained in:
2023-09-14 23:10:30 -06:00
parent a19affcdfd
commit 6aed764160
13 changed files with 695 additions and 22 deletions

View File

@@ -3,9 +3,22 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ParkioModule } from './parkio/parkio.module';
import { IswordModule } from './isword/isword.module';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
@Module({
imports: [ParkioModule, IswordModule],
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration]
}),
ParkioModule,
IswordModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})

View File

@@ -0,0 +1,35 @@
import {
Body,
Controller,
Post,
HttpCode,
Request,
HttpStatus,
UseGuards,
Get,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from './auth.guard';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@HttpCode(HttpStatus.OK)
@Post('login')
signIn(@Body() signInDto: Record<string, any>) {
return this.authService.signIn(signInDto.username, signInDto.password);
}
@UseGuards(AuthGuard)
@Post('hash')
hash(@Body() hashDto: {pass: string, rounds?: number}) {
return this.authService.hash(hashDto.pass, hashDto.rounds)
}
@UseGuards(AuthGuard)
@Get('profile')
getProfile(@Request() req: any) {
return req.user;
}
}

38
src/auth/auth.guard.ts Normal file
View File

@@ -0,0 +1,38 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
try {
const payload = await this.jwtService.verifyAsync(token, {
secret: jwtConstants.secret,
});
// 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers
request['user'] = payload;
} catch {
throw new UnauthorizedException();
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}

20
src/auth/auth.module.ts Normal file
View File

@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UsersModule } from 'src/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
@Module({
imports: [
UsersModule,
JwtModule.register({
global: true,
secret: jwtConstants.secret,
signOptions: { expiresIn: '30m' },
}),
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}

28
src/auth/auth.service.ts Normal file
View File

@@ -0,0 +1,28 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { bcryptConstants } from './constants';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async signIn(username: string, pass: string): Promise<any> {
const user = await this.usersService.findOne(username);
if (!(await bcrypt.compare(pass, user?.password))) {
throw new UnauthorizedException();
}
const payload = { sub: user.userId, username: user.username };
return {
access_token: await this.jwtService.signAsync(payload),
};
}
async hash(pass: string, rounds: number = bcryptConstants.saltRounds): Promise<string> {
return bcrypt.hash(pass, rounds)
}
}

8
src/auth/constants.ts Normal file
View File

@@ -0,0 +1,8 @@
require('dotenv').config()
export const jwtConstants = {
secret: process.env.JWT_SECRET,
};
export const bcryptConstants = {
saltRounds: 10,
};

View File

@@ -0,0 +1,3 @@
export default () => ({
port: parseInt(process.env.PORT ?? '', 10) || 3000,
});

View File

@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
@Module({
providers: [UsersService],
exports: [UsersService]
})
export class UsersModule {}

View File

@@ -0,0 +1,20 @@
import { Injectable } from '@nestjs/common';
require('dotenv').config()
// This should be a real class/interface representing a user entity
export type User = any;
@Injectable()
export class UsersService {
private readonly users = [
{
userId: 1,
username: 'admin',
password: process.env.ADMIN_PASS,
},
];
async findOne(username: string): Promise<User | undefined> {
return this.users.find((user) => user.username === username);
}
}