mirror of
https://github.com/MrSedan/neuro-reply-website.git
synced 2026-01-15 21:19:42 +03:00
feat: auth
This commit is contained in:
48
backend/src/modules/auth/auth.controller.ts
Normal file
48
backend/src/modules/auth/auth.controller.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Request,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
import { JwtGuard } from "./auth.guard";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { LoginDto } from "./dto/login.dto";
|
||||
|
||||
@ApiTags("auth")
|
||||
@Controller("auth")
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@ApiOperation({
|
||||
description: "Login into system",
|
||||
})
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post("login")
|
||||
signIn(@Body() signInDto: LoginDto) {
|
||||
return this.authService.signIn(signInDto.username, signInDto.password);
|
||||
}
|
||||
|
||||
@ApiOperation({
|
||||
description: "Register into system",
|
||||
})
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@Post("register")
|
||||
register(@Body() signInDto: LoginDto) {
|
||||
return this.authService.register(signInDto.username, signInDto.password);
|
||||
}
|
||||
|
||||
@ApiOperation({
|
||||
description: "Get user profile",
|
||||
})
|
||||
@UseGuards(JwtGuard)
|
||||
@Get("profile")
|
||||
@ApiBearerAuth()
|
||||
getProfile(@Request() req) {
|
||||
return req.user;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,31 @@
|
||||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { AuthGuard } from "@nestjs/passport";
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private readonly authService: AuthService,
|
||||
) {}
|
||||
export class JwtGuard extends AuthGuard("jwt") {}
|
||||
// export class AuthGuard implements CanActivate {
|
||||
// constructor(private jwtService: JwtService) {}
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const allowUnauthorizedRequest = this.reflector.get<boolean>('allowUnauthorizedRequest', context.getHandler());
|
||||
// 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: config.auth.jwt_secret,
|
||||
// });
|
||||
|
||||
let token = this.extractTokenFromHeader(request.headers);
|
||||
// request["user"] = payload;
|
||||
// } catch {
|
||||
// throw new UnauthorizedException();
|
||||
// }
|
||||
|
||||
if (!token) {
|
||||
token = request.query.access_token || request.body.access_token;
|
||||
}
|
||||
if (allowUnauthorizedRequest || this.authService.authUserByToken(token)) return true;
|
||||
throw new UnauthorizedException('Unathorized!');
|
||||
}
|
||||
// return true;
|
||||
// }
|
||||
|
||||
private extractTokenFromHeader(headers: any): string | null {
|
||||
if (headers && headers.authorization) {
|
||||
const authHeader = headers.authorization as string;
|
||||
const headerParts = authHeader.split(' ');
|
||||
|
||||
if (headerParts.length === 2 && headerParts[0].toLowerCase() === 'bearer') {
|
||||
return headerParts[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// private extractTokenFromHeader(request: Request): string | undefined {
|
||||
// const [type, token] = request.headers.authorization.split(" ") ?? [];
|
||||
// return type === "Bearer" ? token : undefined;
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { AuthService } from './auth.service';
|
||||
import { HttpBearerStrategy } from './http-bearer.strategy';
|
||||
import { Module } from "@nestjs/common";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
import { PassportModule } from "@nestjs/passport";
|
||||
import { LibsModule } from "libs/libs.module";
|
||||
import { config } from "../../../config";
|
||||
import { AuthController } from "./auth.controller";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { JwtStrategy } from "./strategy/jwt.strategy";
|
||||
|
||||
@Module({
|
||||
imports: [PassportModule.register({ defaultStrategy: 'bearer' })],
|
||||
providers: [HttpBearerStrategy, AuthService],
|
||||
exports: [HttpBearerStrategy, AuthService],
|
||||
imports: [
|
||||
LibsModule,
|
||||
PassportModule,
|
||||
JwtModule.register({
|
||||
secret: config.auth.jwt_secret,
|
||||
global: true,
|
||||
signOptions: { expiresIn: 86000 },
|
||||
}),
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, JwtStrategy],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -1,10 +1,51 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { config } from 'config';
|
||||
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { WebUser } from "libs/database/web_user.entity";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly logger: Logger = new Logger(AuthService.name);
|
||||
authUserByToken(token: string) {
|
||||
return token === config.server.access_token;
|
||||
private readonly logger: Logger = new Logger(AuthService.name);
|
||||
constructor(
|
||||
@InjectRepository(WebUser) private userRepository: Repository<WebUser>,
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async signIn(username: string, pass: string): Promise<{ access_token: string }> {
|
||||
const user = await this.userRepository.findOne({ where: { login: username } });
|
||||
if (!user) throw new UnauthorizedException("User not found");
|
||||
const isMatch = await bcrypt.compare(pass, user.password);
|
||||
if (!isMatch) throw new UnauthorizedException("Wrong password");
|
||||
const payload = { sub: user.uuid, username: user.login };
|
||||
this.logger.log(`User ${user.login} logged in`);
|
||||
return { access_token: await this.jwtService.signAsync(payload) };
|
||||
}
|
||||
|
||||
async validateUserJwt(uuid: string): Promise<any> {
|
||||
const user = await this.userRepository.findOneBy({ uuid: uuid });
|
||||
if (!user) {
|
||||
throw new UnauthorizedException("User not found");
|
||||
}
|
||||
return {
|
||||
login: user.login,
|
||||
telegram_id: user.telegram_id,
|
||||
};
|
||||
}
|
||||
|
||||
async register(username: string, password: string): Promise<{ access_token: string }> {
|
||||
const user = await this.userRepository.findOne({ where: { login: username } });
|
||||
if (user) throw new UnauthorizedException("User already exists");
|
||||
const salt = await bcrypt.genSalt();
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
const newUser = this.userRepository.create({
|
||||
login: username,
|
||||
password: hash,
|
||||
});
|
||||
await this.userRepository.save(newUser);
|
||||
this.logger.log(`User ${username} registered`);
|
||||
const payload = { sub: newUser.uuid, username: newUser.login };
|
||||
return { access_token: await this.jwtService.signAsync(payload) };
|
||||
}
|
||||
}
|
||||
|
||||
6
backend/src/modules/auth/dto/login.dto.ts
Normal file
6
backend/src/modules/auth/dto/login.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty({ description: "User name", example: "test" }) readonly username: string;
|
||||
@ApiProperty({ description: "User password", example: "test" }) readonly password: string;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-http-bearer';
|
||||
import { AuthService } from './auth.service';
|
||||
@Injectable()
|
||||
export class HttpBearerStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private readonly authService: AuthService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async validate(token: string): Promise<boolean> {
|
||||
const user = await this.authService.authUserByToken(token);
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
21
backend/src/modules/auth/strategy/jwt.strategy.ts
Normal file
21
backend/src/modules/auth/strategy/jwt.strategy.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { ExtractJwt, Strategy } from "passport-jwt";
|
||||
import { config } from "../../../../config";
|
||||
import { AuthService } from "../auth.service";
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private authService: AuthService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: config.auth.jwt_secret,
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: { iat: number; sub: string }): Promise<any> {
|
||||
const user = await this.authService.validateUserJwt(payload.sub);
|
||||
if (!user) throw new UnauthorizedException();
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user