Skip to content

Commit

Permalink
Add JWT auth
Browse files Browse the repository at this point in the history
  • Loading branch information
u4aew committed Jan 28, 2024
1 parent e6ebdfb commit 4352db1
Show file tree
Hide file tree
Showing 16 changed files with 308 additions and 14 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.1",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
Expand All @@ -41,6 +45,7 @@
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './modules/auth/auth.module';
import { RegistrationModule } from './modules/registration/registration.module';
import { UserModule } from './modules/user/user.module';
import { MailerModule } from '@nestjs-modules/mailer';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DatabaseExceptionFilter } from './shared/filters/database-exception.filter';
Expand All @@ -28,6 +29,7 @@ import { mailerConfig } from './config/mailer.config';
}),
RegistrationModule,
AuthModule,
UserModule,
],
controllers: [AppController],
providers: [
Expand Down
2 changes: 1 addition & 1 deletion src/database/data-source.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DataSource } from 'typeorm';
import { User } from '../modules/registration/entity/user.entity';
import { User } from '../modules/user/entity/user.entity';

export const AppDataSource = new DataSource({
type: 'postgres',
Expand Down
21 changes: 18 additions & 3 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { Controller } from '@nestjs/common';
// auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';

@Controller('auth')
export class AuthController {}
@Controller('proxy/auth')
export class AuthController {
constructor(private authService: AuthService) {}

@Post('login')
async login(
@Body() authCredentialsDto: AuthCredentialsDto,
): Promise<{ token: string }> {
return this.authService.login(
authCredentialsDto.email,
authCredentialsDto.password,
);
}
}
17 changes: 15 additions & 2 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { UserService } from '../user/user.service'; // Импорт UserService
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt/jwt.strategy';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../user/entity/user.entity';

@Module({
imports: [
TypeOrmModule.forFeature([User]),
JwtModule.register({
secret: 'your-secret-key',
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService, JwtStrategy, UserService],
controllers: [AuthController],
providers: [AuthService]
exports: [AuthService],
})
export class AuthModule {}
25 changes: 23 additions & 2 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
import { Injectable } from '@nestjs/common';
// auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '../user/user.service';

@Injectable()
export class AuthService {}
export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly userService: UserService,
) {}

async login(email: string, password: string): Promise<{ token: string }> {
const user = await this.userService.findByEmail(email);

if (!user || !(await this.userService.validatePassword(user, password))) {
throw new UnauthorizedException('Invalid credentials');
}

const payload = { sub: user.id, email: user.email }; // Можете добавить другие данные пользователя
const token = this.jwtService.sign(payload);

return { token };
}
}
12 changes: 12 additions & 0 deletions src/modules/auth/dto/auth-credentials.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// auth-credentials.dto.ts
import { IsString, IsEmail, MinLength, MaxLength } from 'class-validator';

export class AuthCredentialsDto {
@IsEmail()
email: string;

@IsString()
@MinLength(6)
@MaxLength(20)
password: string;
}
6 changes: 6 additions & 0 deletions src/modules/auth/jwt/jwt-payload.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface JwtPayload {
sub: number; // Уникальный идентификатор пользователя (например, ID пользователя в базе данных)
username: string; // Имя пользователя
roles: string[]; // Роли пользователя (например, "user" или "admin")
// Другие пользовательские поля, которые вы хотите включить в токен
}
28 changes: 28 additions & 0 deletions src/modules/auth/jwt/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JwtPayload } from './jwt-payload.interface'; // Создайте интерфейс JwtPayload для хранения данных в JWT

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'your-secret-key', // Здесь укажите ваш секретный ключ, который используется для подписи и проверки токенов
});
}

async validate(payload: JwtPayload) {
// В этом методе вы можете выполнить проверку данных, хранящихся в токене,
// и вернуть пользователя или выбросить исключение UnauthorizedException, если проверка не пройдет.
// Ваш JwtPayload может содержать, например, ID пользователя.
// Проверьте, что пользователь с указанным ID существует в вашей системе.

// Пример проверки:
// const user = await this.userService.findById(payload.userId);
// if (!user) {
// throw new UnauthorizedException();
// }
// return user;
}
}
2 changes: 1 addition & 1 deletion src/modules/registration/registration.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RegistrationService } from './registration.service';
import { RegistrationController } from './registration.controller';
import { User } from './entity/user.entity';
import { User } from '../user/entity/user.entity';

@Module({
imports: [TypeOrmModule.forFeature([User])],
Expand Down
2 changes: 1 addition & 1 deletion src/modules/registration/registration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entity/user.entity';
import { User } from '../user/entity/user.entity';
import { MailerService } from '@nestjs-modules/mailer';
import { ResponseDto } from '../../shared/dto/response.dto';
import * as bcrypt from 'bcrypt';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import * as bcrypt from 'bcrypt';

@Entity()
export class User {
Expand All @@ -19,4 +20,8 @@ export class User {

@Column({ nullable: true })
emailConfirmationToken: string;

async validatePassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.password);
}
}
4 changes: 4 additions & 0 deletions src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common';

@Controller('user')
export class UserController {}
12 changes: 12 additions & 0 deletions src/modules/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User } from './entity/user.entity';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
21 changes: 21 additions & 0 deletions src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entity/user.entity';
import { Repository } from 'typeorm';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}

async findByEmail(email: string): Promise<User> {
return this.userRepository.findOne({
where: { email },
});
}

async validatePassword(user: User, password: string): Promise<boolean> {
return user.validatePassword(password);
}
}
Loading

0 comments on commit 4352db1

Please sign in to comment.