Aws Cognito, Passport 및 NestJs를 사용한 인증(2부)

Nest Js 프로젝트 설정


nest new pokemon-app 로 nest js 프로젝트의 새 인스턴스를 부팅하여 시작하겠습니다.

또한 몇 가지 추가 라이브러리를 설치해야 합니다.
  • Cognito 서비스와 통신할 수 있도록, Amazon Cognito Identity SDK
  • 조건이 있는 요청 본문 매개변수의 유효성을 검사하는 경우, class-validator
  • 일부 기준에 따라 개체 직렬화/역직렬화 활성화, class-transformer
  • JWT 토큰의 서명 키를 가져오려면jwks-rsa
  • 요청을 인증하기 위한 인증 미들웨어 역할을 하는 여권Passport
  • JWT용 여권 전략passport-jwt
  • nestjs용 여권 유틸리티@nestjs/passport
  • 여권 JWT 유형@types/passport-jwt

  • 패키지.json:




    ...
    
     "dependencies": {
        "@nestjs/common": "^9.0.0",
        "@nestjs/config": "^2.2.0",
        "@nestjs/core": "^9.0.0",
        "@nestjs/platform-express": "^9.0.0",
        "amazon-cognito-identity-js": "^5.2.10",
        "@nestjs/passport": "^9.0.0",
        "@types/passport-jwt": "^3.0.7",
        "class-transformer": "^0.5.1",
        "class-validator": "^0.13.2",
        "jwks-rsa": "^2.1.5",
        "passport": "^0.6.0",
        "passport-jwt": "^4.0.0",
        "reflect-metadata": "^0.1.13",
        "rimraf": "^3.0.2",
        "rxjs": "^7.2.0"
      }
    
    


    또한 nestjs에서 환경 파일을 관리하려면 npm i @nestjs/config를 통해 nest 구성 모듈을 설치해야 합니다.
    이 가이드에서는 간단하게 유지하고 전역으로 참조하며 종속성 주입 대신 process.env를 직접 사용합니다. 더 많은 문서를 찾을 수 있습니다here.

    루트 폴더에 .development.env 파일을 만드는 것을 잊지 마십시오.

    앱 모듈.ts:




    ...
    @Module({
      imports: [
        ConfigModule.forRoot({
          envFilePath: '.development.env',
          isGlobal: true,
        }),
        PokemonModule,
        AuthModule,
      ],
      controllers: [],
      providers: [],
    })
    export class AppModule {}
    
    



    등록 및 인증



    그런 다음 루트 디렉토리로 이동하여 nest g module pokemon로 모듈을 부팅하고 nest g controller pokemonnest g service pokemon로 컨트롤러와 하나의 서비스를 생성합니다.

    이제 pokemon 디렉토리 아래에 pokemon 인터페이스를 생성하고 내보냅니다.

    pokemon.interface.ts:




    
    export interface Pokemon {
      readonly name: string;
      readonly type: string;
    } 
    
    


    서비스에서 포켓몬을 나열하는 메서드를 생성해 보겠습니다. (가져올 끝점의 예를 갖는 것은 정적입니다)

    포켓몬.service.ts:




    
    @Injectable()
    export class PokemonService {
      listAllPokemons(): Array<Pokemon> {
        return [
          { name: 'Pikachu', type: 'Electric' },
          { name: 'Volpix', type: 'Fire' },
          { name: 'Flabébé', type: 'Fairy' },
        ];
      }
    }
    
    


    포켓몬 컨트롤러에서 끝점 경로를 참조하고 소비할 PokemonService를 주입해 보겠습니다.

    포켓몬.컨트롤러.ts:




    ...
    
    import { PokemonService } from './pokemon.service';
    
    @Controller('api/v1/pokemons')
    export class PokemonController {
      constructor(private readonly pokemonService: PokemonService) {}
    
      @Get()
      listAllPokemons(): Array<Pokemon> {
        return this.pokemonService.listAllPokemons();
      }
    }
    
    

    npm run start:dev를 실행할 수 있습니다. 모든 것이 제대로 빌드되면 포켓몬을 http://localhost:3000/api/v1/pokemons에 나열해야 합니다.



    이제 우리는 인증된 사용자가 해당 끝점에서 정보를 가져올 수 있도록 인증 모듈을 빌드할 것입니다. nest g module auth로 또 다른 모듈을 생성하고 nest g controller auth로 또 다른 컨트롤러를 생성해 봅시다.

    다음으로 사용자 등록 및 로그인을 위해 auth 폴더 아래에 DTOs 을 생성해 보겠습니다.

    인증-로그인-사용자:




    ...
    
    import { IsEmail, Matches } from 'class-validator';
    
    export class AuthLoginUserDto {
      @IsEmail()
      email: string;
    
      /* Minimum eight characters, at least one uppercase letter, one lowercase letter, one number, and one special character */
    
      @Matches(
        /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$&+,:;=?@#|'<>.^*()%!-])[A-Za-z\d@$&+,:;=?@#|'<>.^*()%!-]{8,}$/,
        { message: 'invalid password' },
      )
      password: string;
    }
    
    


    인증-등록-사용자:




    ...
    
    import { IsEmail, IsString, Matches } from 'class-validator';
    
    export class AuthRegisterUserDto {
      @IsString()
      name: string;
    
      @IsEmail()
      email: string;
    
      /* Minimum eight characters, at least one uppercase letter, one lowercase letter, one number, and one special character */
    
      @Matches(
        /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$&+,:;=?@#|'<>.^*()%!-])[A-Za-z\d@$&+,:;=?@#|'<>.^*()%!-]{8,}$/,
        { message: 'invalid password' },
      )
      password: string;
    
    }
    
    


    등록 및 인증 방법으로 Cognito 서비스를 작성한 후 인증 모듈 내부에 새 서비스를 생성하고 해당 공급자의 모듈에서 참조하는 것을 잊지 마십시오.

    aws-cognito.service.ts:




    
    import { Injectable } from '@nestjs/common';
    import {
      AuthenticationDetails,
      CognitoUser,
      CognitoUserAttribute,
      CognitoUserPool,
    } from 'amazon-cognito-identity-js';
    import { AuthLoginUserDto } from './dtos/auth-login-user.dto';
    import { AuthRegisterUserDto } from './dtos/auth-register-user.dto';
    
    @Injectable()
    export class AwsCognitoService {
      private userPool: CognitoUserPool;
    
      constructor() {
        this.userPool = new CognitoUserPool({
          UserPoolId: process.env.AWS_COGNITO_USER_POOL_ID,
          ClientId: process.env.AWS_COGNITO_CLIENT_ID,
        });
      }
    
      async registerUser(authRegisterUserDto: AuthRegisterUserDto) {
        const { name, email, password } = authRegisterUserDto;
    
        return new Promise((resolve, reject) => {
          this.userPool.signUp(
            email,
            password,
            [
              new CognitoUserAttribute({
                Name: 'name',
                Value: name,
              }),
            ],
            null,
            (err, result) => {
              if (!result) {
                reject(err);
              } else {
                resolve(result.user);
              }
            },
          );
        });
      }
    
      async authenticateUser(authLoginUserDto: AuthLoginUserDto) {
        const { email, password } = authLoginUserDto;
        const userData = {
          Username: email,
          Pool: this.userPool,
        };
    
        const authenticationDetails = new AuthenticationDetails({
          Username: email,
          Password: password,
        });
    
        const userCognito = new CognitoUser(userData);
    
        return new Promise((resolve, reject) => {
          userCognito.authenticateUser(authenticationDetails, {
            onSuccess: (result) => {
              resolve(result);
            },
            onFailure: (err) => {
              reject(err);
            },
          });
        });
      }
    }
    
    


    이제 우리는 가이드의 첫 부분에서 가지고 있던 development.envAWS_COGNITO_USER_POOL_ID에 대한 값을 AWS_COGNITO_CLIENT_ID에 저장해야 합니다.
    auth.controller.ts 에서 이제 AWS Cognito 서비스를 삽입하고 사용자를 등록 및 인증할 수 있습니다.

    auth.controller.ts




    
    import {
      Body,
      Controller,
      Post,
      UsePipes,
      ValidationPipe,
    } from '@nestjs/common';
    import { AwsCognitoService } from './aws-cognito.service';
    import { AuthLoginUserDto } from './dtos/auth-login-user.dto';
    import { AuthRegisterUserDto } from './dtos/auth-register-user.dto';
    
    @Controller('api/v1/auth')
    export class AuthController {
      constructor(private awsCognitoService: AwsCognitoService) {}
    
      @Post('/register')
      async register(@Body() authRegisterUserDto: AuthRegisterUserDto) {
        return await this.awsCognitoService.registerUser(authRegisterUserDto);
      }
    
      @Post('/login')
      @UsePipes(ValidationPipe)
      async login(@Body() authLoginUserDto: AuthLoginUserDto) {
        return await this.awsCognitoService.authenticateUser(authLoginUserDto);
      }
    }
    
    


    npm run start:dev로 애플리케이션을 시작하고 모든 것이 오류 없이 시작되면 레지스트리 엔드포인트를 통해 작동하는지 확인할 수 있습니다.







    작동하는 것 같습니다. 사용자가 확인했습니다. 로그인을 테스트해 보겠습니다.



    이것이 두 번째 부분의 전부입니다. 세 번째 부분에서는 Passport 및 Jwt 인증을 사용하여 끝점과 리소스를 보호합니다. 계속 지켜봐주세요 😊

    좋은 웹페이지 즐겨찾기