Last active
June 18, 2021 11:30
-
-
Save thisismydesign/09b0140901b6f1536cb9a047eeae51d6 to your computer and use it in GitHub Desktop.
Cognito via OAuth2 in NestJS: keep user data in your app, let Cognito handle passwords and social login /1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; | |
import { Response, Request } from 'express'; | |
import { CognitoOauthGuard } from './cognito-oauth.guard'; | |
@Controller('auth/cognito') | |
export class CognitoOauthController { | |
constructor(private jwtAuthService: JwtAuthService) {} | |
@Get() | |
@UseGuards(CognitoOauthGuard) | |
async cognitoAuth(@Req() _req: Request) { | |
// Guard redirects | |
} | |
@Get('redirect') | |
@UseGuards(CognitoOauthGuard) | |
async cognitoAuthRedirect(@Req() req: Request, @Res() res: Response) { | |
// Here we can issue a JWT token to manage the user session from the app | |
// For now, we'll just show the user object | |
return req.user; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@nestjs/common'; | |
import { AuthGuard } from '@nestjs/passport'; | |
@Injectable() | |
export class CognitoOauthGuard extends AuthGuard('cognito') {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Module } from '@nestjs/common'; | |
import { CognitoOauthController } from './cognito-oauth.controller'; | |
import { CognitoOauthStrategy } from './cognito-oauth.strategy'; | |
@Module({ | |
imports: [], | |
controllers: [CognitoOauthController], | |
providers: [CognitoOauthStrategy], | |
}) | |
export class CognitoOauthModule {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PassportStrategy } from '@nestjs/passport'; | |
import { Strategy } from 'passport-oauth2'; | |
import { Injectable } from '@nestjs/common'; | |
import { ConfigService } from '@nestjs/config'; | |
import axios from 'axios'; | |
@Injectable() | |
export class CognitoOauthStrategy extends PassportStrategy( | |
Strategy, | |
'cognito', | |
) { | |
private domain: string; | |
constructor( | |
configService: ConfigService, | |
) { | |
super({ | |
authorizationURL: CognitoOauthStrategy.authorizationUrl( | |
configService.get<string>('OAUTH_COGNITO_DOMAIN') as string, | |
), | |
tokenURL: CognitoOauthStrategy.tokenUrl( | |
configService.get<string>('OAUTH_COGNITO_DOMAIN') as string, | |
), | |
clientID: configService.get<string>('OAUTH_COGNITO_ID'), | |
clientSecret: configService.get<string>('OAUTH_COGNITO_SECRET'), | |
callbackURL: configService.get<string>('OAUTH_COGNITO_REDIRECT_URL'), | |
}); | |
this.domain = configService.get<string>('OAUTH_COGNITO_DOMAIN') as string; | |
} | |
static oAuthBaseUrl(domain: string): string { | |
return `${domain}/oauth2`; | |
} | |
static authorizationUrl(domain: string): string { | |
return `${this.oAuthBaseUrl(domain)}/authorize`; | |
} | |
static tokenUrl(domain: string): string { | |
return `${this.oAuthBaseUrl(domain)}/token`; | |
} | |
static userInfoUrl(domain: string): string { | |
return `${this.oAuthBaseUrl(domain)}/userInfo`; | |
} | |
static logoutUrl(domain: string, clientId: string, redirect: string): string { | |
return `${domain}/logout?client_id=${clientId}&logout_uri=${redirect}`; | |
} | |
async validate(accessToken: string) { | |
// Here the `id_token` is also received: https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html | |
// But it's not supported by passport-oauth2, only `access_token` is received | |
// Therefore another call is made to the userinfo endpoint | |
const userinfo = ( | |
await axios.get(CognitoOauthStrategy.userInfoUrl(this.domain), { | |
headers: { Authorization: `Bearer ${accessToken}` }, | |
}) | |
).data; | |
// Here a custom User object is returned. In the the repo I'm using a UsersService with repository pattern, learn more here: https://docs.nestjs.com/techniques/database | |
return { | |
provider: 'cognito', | |
providerId: userinfo.username, | |
name: userinfo.name, | |
username: userinfo.email, | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment