import { Controller, Get, Inject, Param, Post, Query, Req, Res, UseFilters, UseGuards, } from "@nestjs/common"; import { User } from "@prisma/client"; import { Request, Response } from "express"; import { nanoid } from "nanoid"; import { AuthService } from "../auth/auth.service"; import { GetUser } from "../auth/decorator/getUser.decorator"; import { JwtGuard } from "../auth/guard/jwt.guard"; import { ConfigService } from "../config/config.service"; import { OAuthCallbackDto } from "./dto/oauthCallback.dto"; import { ErrorPageExceptionFilter } from "./filter/errorPageException.filter"; import { OAuthGuard } from "./guard/oauth.guard"; import { ProviderGuard } from "./guard/provider.guard"; import { OAuthService } from "./oauth.service"; import { OAuthProvider } from "./provider/oauthProvider.interface"; import { OAuthExceptionFilter } from "./filter/oauthException.filter"; @Controller("oauth") export class OAuthController { constructor( private authService: AuthService, private oauthService: OAuthService, private config: ConfigService, @Inject("OAUTH_PROVIDERS") private providers: Record>, ) {} @Get("available") available() { return this.oauthService.available(); } @Get("status") @UseGuards(JwtGuard) async status(@GetUser() user: User) { return this.oauthService.status(user); } @Get("auth/:provider") @UseGuards(ProviderGuard) @UseFilters(ErrorPageExceptionFilter) async auth( @Param("provider") provider: string, @Res({ passthrough: true }) response: Response, ) { const state = nanoid(16); const url = await this.providers[provider].getAuthEndpoint(state); response.cookie(`oauth_${provider}_state`, state, { sameSite: "lax" }); response.redirect(url); } @Get("callback/:provider") @UseGuards(ProviderGuard, OAuthGuard) @UseFilters(ErrorPageExceptionFilter, OAuthExceptionFilter) async callback( @Param("provider") provider: string, @Query() query: OAuthCallbackDto, @Req() request: Request, @Res({ passthrough: true }) response: Response, ) { const oauthToken = await this.providers[provider].getToken(query); const user = await this.providers[provider].getUserInfo(oauthToken, query); const id = await this.authService.getIdOfCurrentUser(request); if (id) { await this.oauthService.link( id, provider, user.providerId, user.providerUsername, ); response.redirect(this.config.get("general.appUrl") + "/account"); } else { const token: { accessToken?: string; refreshToken?: string; loginToken?: string; } = await this.oauthService.signIn(user, request.ip); if (token.accessToken) { this.authService.addTokensToResponse( response, token.refreshToken, token.accessToken, ); response.redirect(this.config.get("general.appUrl")); } else { response.redirect( this.config.get("general.appUrl") + `/auth/totp/${token.loginToken}`, ); } } } @Post("unlink/:provider") @UseGuards(JwtGuard, ProviderGuard) @UseFilters(ErrorPageExceptionFilter) unlink(@GetUser() user: User, @Param("provider") provider: string) { return this.oauthService.unlink(user, provider); } }