feat: add authentication using Discord oauth2 and JWT
Temporarily disabling GraphQL using Mercuris due to moving to Express (previously fastify)
This commit is contained in:
parent
782698788b
commit
fbf7272526
@ -14,6 +14,7 @@
|
|||||||
"preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\""
|
"preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/passport": "^3.0.1",
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
"@nestjs/config": "^3.3.0",
|
"@nestjs/config": "^3.3.0",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"@nestjs/jwt": "^10.2.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/mercurius": "^12.2.1",
|
"@nestjs/mercurius": "^12.2.1",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
|
"@nestjs/platform-express": "^10.4.11",
|
||||||
"@nestjs/platform-fastify": "^10.4.9",
|
"@nestjs/platform-fastify": "^10.4.9",
|
||||||
"@prisma/client": "5.22.0",
|
"@prisma/client": "5.22.0",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
@ -29,6 +31,9 @@
|
|||||||
"graphql-tools": "^9.0.4",
|
"graphql-tools": "^9.0.4",
|
||||||
"graphql-ws": "^5.16.0",
|
"graphql-ws": "^5.16.0",
|
||||||
"mercurius": "14",
|
"mercurius": "14",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-discord": "^0.1.4",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
"prisma": "^5.22.0",
|
"prisma": "^5.22.0",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "^0.2.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
@ -39,6 +44,7 @@
|
|||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "^10.0.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
|
"@types/passport-discord": "^0.1.14",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"eslint": "^9.0.0",
|
"eslint": "^9.0.0",
|
||||||
|
@ -8,6 +8,7 @@ import { AppService } from "./app.service";
|
|||||||
import { UserModule } from "./user/user.module";
|
import { UserModule } from "./user/user.module";
|
||||||
|
|
||||||
import { GraphqlOptions } from "./graphql/graphqlOptions";
|
import { GraphqlOptions } from "./graphql/graphqlOptions";
|
||||||
|
import { AuthModule } from './auth/auth.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -18,11 +19,12 @@ import { GraphqlOptions } from "./graphql/graphqlOptions";
|
|||||||
global: true,
|
global: true,
|
||||||
secret: process.env.JWT_SECRET,
|
secret: process.env.JWT_SECRET,
|
||||||
}),
|
}),
|
||||||
GraphQLModule.forRootAsync<MercuriusDriverConfig>({
|
// GraphQLModule.forRootAsync<MercuriusDriverConfig>({
|
||||||
driver: MercuriusDriver,
|
// driver: MercuriusDriver,
|
||||||
useClass: GraphqlOptions,
|
// useClass: GraphqlOptions,
|
||||||
}),
|
// }),
|
||||||
UserModule,
|
UserModule,
|
||||||
|
AuthModule,
|
||||||
],
|
],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
33
src/auth/Strategy/discord.strategy.ts
Normal file
33
src/auth/Strategy/discord.strategy.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { JwtService } from "@nestjs/jwt";
|
||||||
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
|
import { Profile, Strategy } from "passport-discord";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
||||||
|
constructor(private jwtService: JwtService) {
|
||||||
|
super({
|
||||||
|
clientID: process.env.DISCORD_CLIENT_ID,
|
||||||
|
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
||||||
|
callbackURL: process.env.DISCORD_CALLBACK_URL,
|
||||||
|
scope: ["identify", "email"]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(
|
||||||
|
_accessToken: string,
|
||||||
|
_refreshToken: string,
|
||||||
|
profile: Profile,
|
||||||
|
done: Function,
|
||||||
|
) {
|
||||||
|
const jwtPayload = {
|
||||||
|
id: profile.id,
|
||||||
|
username: profile.username,
|
||||||
|
email: profile.email,
|
||||||
|
};
|
||||||
|
|
||||||
|
const jwt = this.jwtService.sign(jwtPayload);
|
||||||
|
|
||||||
|
done(null, { jwt });
|
||||||
|
}
|
||||||
|
}
|
19
src/auth/Strategy/jwt.strategy.ts
Normal file
19
src/auth/Strategy/jwt.strategy.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
|
import { Profile } from "passport-discord";
|
||||||
|
import { Strategy, ExtractJwt } from "passport-jwt";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JWTStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
ignoreExipration: false,
|
||||||
|
secretOrKey: process.env.JWT_SECRET,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(profile: Profile): Promise<any> {
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
36
src/auth/auth.controller.ts
Normal file
36
src/auth/auth.controller.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Controller, Get, Req, Res, UseGuards } from "@nestjs/common";
|
||||||
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
|
import { URLSearchParams } from "node:url";
|
||||||
|
|
||||||
|
@Controller("auth")
|
||||||
|
export class AuthController {
|
||||||
|
@Get("providers")
|
||||||
|
Providers() {
|
||||||
|
const discordOauth2Params = new URLSearchParams({
|
||||||
|
client_id: process.env.DISCORD_CLIENT_ID,
|
||||||
|
response_type: "code",
|
||||||
|
redirect_uri: process.env.DISCORD_CALLBACK_URL,
|
||||||
|
scope: "identify email",
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
discord: {
|
||||||
|
url: `https://discord.com/oauth2/authorize?${discordOauth2Params.toString()}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("discord/callback")
|
||||||
|
@UseGuards(AuthGuard("discord"))
|
||||||
|
CallbackDiscord(@Req() req, @Res() res) {
|
||||||
|
const { user } = req;
|
||||||
|
|
||||||
|
res.send(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("profile")
|
||||||
|
@UseGuards(AuthGuard("jwt"))
|
||||||
|
Profile(@Req() req) {
|
||||||
|
return req.user;
|
||||||
|
}
|
||||||
|
}
|
12
src/auth/auth.module.ts
Normal file
12
src/auth/auth.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { AuthController } from "./auth.controller";
|
||||||
|
import { AuthService } from "./auth.service";
|
||||||
|
|
||||||
|
import { DiscordStrategy } from "./Strategy/discord.strategy";
|
||||||
|
import { JWTStrategy } from "./Strategy/jwt.strategy";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [AuthController],
|
||||||
|
providers: [AuthService, DiscordStrategy, JWTStrategy],
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
4
src/auth/auth.service.ts
Normal file
4
src/auth/auth.service.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {}
|
@ -1,12 +1,8 @@
|
|||||||
import { NestFactory } from "@nestjs/core";
|
import { NestFactory } from "@nestjs/core";
|
||||||
import { AppModule } from "./app.module";
|
import { AppModule } from "./app.module";
|
||||||
import {
|
|
||||||
FastifyAdapter,
|
|
||||||
NestFastifyApplication,
|
|
||||||
} from "@nestjs/platform-fastify";
|
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule, new FastifyAdapter());
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: "*",
|
origin: "*",
|
||||||
|
Loading…
Reference in New Issue
Block a user