NestJS实现Google与Twitch多认证及账号关联技术问询
NestJS 多第三方账号关联方案实现
需求背景
在NestJS中实现基于Google和Twitch的多第三方认证,不采用本地策略,不存储用户个人凭据。核心需求是允许用户关联两个平台账号,以此解锁更多功能。
已实现基础代码
Google认证策略(google.strategy.ts)
// google.strategy.ts @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor( private userService: UsersService, private configService: ConfigService, ) { super({ clientID: configService.get<string>('GOOGLE_ID'), clientSecret: configService.get<string>('GOOGLE_SECRET'), callbackURL: `${configService.get<string>( 'BASE_URL_APP', )}/auth/google/callback`, scope: ['email', 'profile'], }); } async validate( accessToken: string, refreshToken: string, profile: any, cb: Function, ): Promise<any> { let user = await this.userService.findOneByGoogleProviderId(profile.id); if (!user) { user = await this.userService.create({ creatorsFollowed: [], providerAccess: [ { accessToken: accessToken, platform: Platform.GOOGLE, providerUserId: profile.id, refreshToken: refreshToken, email: profile['_json']['email'], }, ], firstname: profile['_json']['given_name'], lastName: profile['_json']['family_name'], }); } return cb(null, user); } }
Auth控制器中的Google认证端点
@Get('google') @UseGuards(AuthGuard('google')) async googleAuth() { } @Get('google/callback') @UseGuards(AuthGuard('google')) async googleAuthCallback(@Req() req) { const providerPurgedData = req.user.providerAccess.map( ({ accessToken, refreshToken, ...keepAttrs }) => keepAttrs, ); const payload = { providersData: providerPurgedData, firstname: req.user?.firstname, lastname: req.user?.lastname, }; return { accessToken: this.jwtService.sign(payload) }; }
新增需求与初始尝试
需要复用现有端点,实现:当用户携带其他平台(如Twitch)的有效JWT访问Google/Twitch认证端点时,不生成新令牌,仅在数据库中关联两个账号。
最初尝试给google/callback和twitch/callback添加中间件,但不知道如何正确验证JWT,也考虑过重写Guard。初始中间件代码如下:
// middleware import { Injectable, NestMiddleware } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Request, Response } from 'express'; @Injectable() export class LinkProviderAccountMiddleware implements NestMiddleware { constructor(private jwtService: JwtService) {} use(req: Request, res: Response, next: () => void) { const bearer = req.headers['authorization'].split(' ')[1]; if (this.jwtService.verify(bearer)) { // link account res.status(200).json({ msg: 'account linked' }); } next(); } }
更新解决方案(2023/06/25)
最终通过中间件结合策略配置实现需求,更新后的代码如下:
验证用户登录状态的中间件
//middleware import { Injectable, NestMiddleware, UnauthorizedException, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Request, Response } from 'express'; @Injectable() export class CheckUserLoggedInMiddleware implements NestMiddleware { constructor(private jwtService: JwtService) {} use(req: Request, res: Response, next: () => void) { const token = req.headers.authorization?.split(' ')[1]; if (token) { try { const decoded = this.jwtService.verify(token); req.user = decoded; return next(); } catch (error) { throw new UnauthorizedException(); } } next(); } }
更新后的Google策略
// google.strategy @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor( private userService: UsersService, private configService: ConfigService, ) { super({ clientID: configService.get<string>('GOOGLE_ID'), clientSecret: configService.get<string>('GOOGLE_SECRET'), callbackURL: `${configService.get<string>( 'BASE_URL_APP', )}/auth/google/callback`, scope: ['email', 'profile'], passReqToCallback: true, // 新增配置 }); } }
现在用户携带有效JWT调用Twitch或Google认证端点时,策略可以从请求中获取到用户信息,进而完成账号关联操作。
内容的提问来源于stack exchange,提问作者Yokido
相关产品推荐
相关产品推荐

