feat: Implement refresh jwt token

This commit is contained in:
M1000fr 2024-12-02 16:39:18 +01:00
parent 6521da705b
commit ab51873493
8 changed files with 137 additions and 15 deletions

View File

@ -0,0 +1,8 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsJWT } from "class-validator";
export class ResfreshDTO {
@ApiProperty()
@IsJWT()
refreshToken: string;
}

View File

@ -0,0 +1,5 @@
import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
@Injectable()
export class RefreshJwtAuthGuard extends AuthGuard("refresh") {}

View File

@ -1,13 +1,15 @@
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
import { PassportStrategy } from "@nestjs/passport";
import { Profile, Strategy } from "passport-discord";
import { AuthService } from "../auth.service";
@Injectable()
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
configService: ConfigService;
constructor(
private readonly jwtService: JwtService,
private readonly authService: AuthService,
configService: ConfigService,
) {
super({
@ -20,6 +22,8 @@ export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
),
scope: ["identify", "email"],
});
this.configService = configService;
}
async validate(
@ -28,10 +32,9 @@ export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
profile: Profile,
done: Function,
) {
const jwt = this.jwtService.sign({
id: profile.id,
});
const accessToken = this.authService.accessToken({ id: profile.id });
const refreshToken = this.authService.refreshToken({ id: profile.id });
done(null, { jwt });
done(null, { accessToken, refreshToken });
}
}

View File

@ -9,7 +9,7 @@ import { ConfigService } from "@nestjs/config";
export class JWTStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly userService: UserService,
private readonly configService: ConfigService,
configService: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),

View File

@ -0,0 +1,30 @@
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { User } from "@prisma/client";
import { Strategy, ExtractJwt } from "passport-jwt";
import { UserService } from "@Modules/user/user.service";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class RefreshJWTStrategy extends PassportStrategy(Strategy, "refresh") {
constructor(
private readonly userService: UserService,
configService: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExipration: false,
secretOrKey: configService.get<string>("JWT.refresh.secret"),
});
}
async validate(payload: any): Promise<User> {
const user = await this.userService.findById(payload.id);
if (!user) {
throw new UnauthorizedException("User not found");
}
return user;
}
}

View File

@ -1,18 +1,47 @@
import { Controller, Get, Req, Res, UseGuards } from "@nestjs/common";
import {
Body,
Controller,
Get,
Post,
Req,
Res,
UseGuards,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { ApiBearerAuth, ApiBody, ApiOkResponse } from "@nestjs/swagger";
import { URLSearchParams } from "node:url";
import { ResfreshDTO } from "./Dto/refresh.dto";
import { DiscordAuthGuard } from "./Guards/discord.guard";
import { RefreshJwtAuthGuard } from "./Guards/refresh.guard";
import { AuthService } from "./auth.service";
@Controller("auth")
export class AuthController {
constructor(private readonly configService: ConfigService) {}
constructor(
private readonly configService: ConfigService,
private readonly authService: AuthService,
) {}
@Get("providers")
getProviders() {
@ApiOkResponse({
description: "List of OAuth2 providers",
example: {
discord: { url: "https://discord.com/oauth2/authorize?..." },
keycloak: { url: "https://keycloak.com/oauth2/authorize?..." },
},
})
getProviders() {
const discordOauth2Params = new URLSearchParams({
client_id: this.configService.get<string>("oauth2.discord.clientId"),
client_id: this.configService.get<string>(
"oauth2.discord.clientId",
),
response_type: "code",
redirect_uri: this.configService.get<string>("oauth2.discord.callbackUrl"),
redirect_uri: this.configService.get<string>(
"oauth2.discord.callbackUrl",
),
scope: "identify email",
});
@ -25,9 +54,26 @@ export class AuthController {
@Get("discord/callback")
@UseGuards(DiscordAuthGuard)
CallbackDiscord(@Req() req, @Res() res) {
@ApiOkResponse({
description: "Return JWT token",
example: {
accessToken: "Hello World!",
refreshtoken: "Hello World!",
},
})
callbackDiscord(@Req() req, @Res() res) {
const { user } = req;
res.send(user);
}
@Get("refresh")
@UseGuards(RefreshJwtAuthGuard)
@ApiBearerAuth()
@ApiBody({
type: ResfreshDTO,
})
refresh(@Req() req) {
return this.authService.accessToken(req.user);
}
}

View File

@ -5,10 +5,11 @@ import { AuthService } from "./auth.service";
import { UserModule } from "@Modules/user/user.module";
import { DiscordStrategy } from "./Strategy/discord.strategy";
import { JWTStrategy } from "./Strategy/jwt.strategy";
import { RefreshJWTStrategy } from "./Strategy/refresh.strategy";
@Module({
imports: [UserModule],
controllers: [AuthController],
providers: [AuthService, DiscordStrategy, JWTStrategy],
providers: [AuthService, DiscordStrategy, JWTStrategy, RefreshJWTStrategy],
})
export class AuthModule {}

View File

@ -1,4 +1,33 @@
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
@Injectable()
export class AuthService {}
export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
accessToken(user: { id: string }) {
const payload = { id: user.id };
return {
accessToken: this.jwtService.sign(payload, {
secret: this.configService.get<string>("JWT.secret"),
expiresIn: this.configService.get<string>("JWT.expiresIn"),
}),
};
}
refreshToken(user: { id: string }) {
const payload = { id: user.id };
return {
refreshToken: this.jwtService.sign(payload, {
secret: this.configService.get<string>("JWT.refresh.secret"),
expiresIn: this.configService.get<string>(
"JWT.refresh.expiresIn",
),
}),
};
}
}