feat: Implement refresh jwt token
This commit is contained in:
parent
6521da705b
commit
ab51873493
8
src/modules/auth/Dto/refresh.dto.ts
Normal file
8
src/modules/auth/Dto/refresh.dto.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsJWT } from "class-validator";
|
||||||
|
|
||||||
|
export class ResfreshDTO {
|
||||||
|
@ApiProperty()
|
||||||
|
@IsJWT()
|
||||||
|
refreshToken: string;
|
||||||
|
}
|
5
src/modules/auth/Guards/refresh.guard.ts
Normal file
5
src/modules/auth/Guards/refresh.guard.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RefreshJwtAuthGuard extends AuthGuard("refresh") {}
|
@ -1,13 +1,15 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { JwtService } from "@nestjs/jwt";
|
|
||||||
import { PassportStrategy } from "@nestjs/passport";
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
import { Profile, Strategy } from "passport-discord";
|
import { Profile, Strategy } from "passport-discord";
|
||||||
|
import { AuthService } from "../auth.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
||||||
|
configService: ConfigService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly jwtService: JwtService,
|
private readonly authService: AuthService,
|
||||||
configService: ConfigService,
|
configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
@ -20,6 +22,8 @@ export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
|||||||
),
|
),
|
||||||
scope: ["identify", "email"],
|
scope: ["identify", "email"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.configService = configService;
|
||||||
}
|
}
|
||||||
|
|
||||||
async validate(
|
async validate(
|
||||||
@ -28,10 +32,9 @@ export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
|||||||
profile: Profile,
|
profile: Profile,
|
||||||
done: Function,
|
done: Function,
|
||||||
) {
|
) {
|
||||||
const jwt = this.jwtService.sign({
|
const accessToken = this.authService.accessToken({ id: profile.id });
|
||||||
id: profile.id,
|
const refreshToken = this.authService.refreshToken({ id: profile.id });
|
||||||
});
|
|
||||||
|
|
||||||
done(null, { jwt });
|
done(null, { accessToken, refreshToken });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import { ConfigService } from "@nestjs/config";
|
|||||||
export class JWTStrategy extends PassportStrategy(Strategy) {
|
export class JWTStrategy extends PassportStrategy(Strategy) {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly configService: ConfigService,
|
configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
30
src/modules/auth/Strategy/refresh.strategy.ts
Normal file
30
src/modules/auth/Strategy/refresh.strategy.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 { ConfigService } from "@nestjs/config";
|
||||||
|
import { ApiBearerAuth, ApiBody, ApiOkResponse } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { URLSearchParams } from "node:url";
|
import { URLSearchParams } from "node:url";
|
||||||
|
|
||||||
|
import { ResfreshDTO } from "./Dto/refresh.dto";
|
||||||
|
|
||||||
import { DiscordAuthGuard } from "./Guards/discord.guard";
|
import { DiscordAuthGuard } from "./Guards/discord.guard";
|
||||||
|
import { RefreshJwtAuthGuard } from "./Guards/refresh.guard";
|
||||||
|
import { AuthService } from "./auth.service";
|
||||||
|
|
||||||
@Controller("auth")
|
@Controller("auth")
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(private readonly configService: ConfigService) {}
|
constructor(
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get("providers")
|
@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({
|
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",
|
response_type: "code",
|
||||||
redirect_uri: this.configService.get<string>("oauth2.discord.callbackUrl"),
|
redirect_uri: this.configService.get<string>(
|
||||||
|
"oauth2.discord.callbackUrl",
|
||||||
|
),
|
||||||
scope: "identify email",
|
scope: "identify email",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -25,9 +54,26 @@ export class AuthController {
|
|||||||
|
|
||||||
@Get("discord/callback")
|
@Get("discord/callback")
|
||||||
@UseGuards(DiscordAuthGuard)
|
@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;
|
const { user } = req;
|
||||||
|
|
||||||
res.send(user);
|
res.send(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("refresh")
|
||||||
|
@UseGuards(RefreshJwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiBody({
|
||||||
|
type: ResfreshDTO,
|
||||||
|
})
|
||||||
|
refresh(@Req() req) {
|
||||||
|
return this.authService.accessToken(req.user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ import { AuthService } from "./auth.service";
|
|||||||
import { UserModule } from "@Modules/user/user.module";
|
import { UserModule } from "@Modules/user/user.module";
|
||||||
import { DiscordStrategy } from "./Strategy/discord.strategy";
|
import { DiscordStrategy } from "./Strategy/discord.strategy";
|
||||||
import { JWTStrategy } from "./Strategy/jwt.strategy";
|
import { JWTStrategy } from "./Strategy/jwt.strategy";
|
||||||
|
import { RefreshJWTStrategy } from "./Strategy/refresh.strategy";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UserModule],
|
imports: [UserModule],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
providers: [AuthService, DiscordStrategy, JWTStrategy],
|
providers: [AuthService, DiscordStrategy, JWTStrategy, RefreshJWTStrategy],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
@ -1,4 +1,33 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { JwtService } from "@nestjs/jwt";
|
||||||
|
|
||||||
@Injectable()
|
@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",
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user