만자의 개발일지

[AWS] Cognito 회원 가입 및 로그인 기능 구현하기(feat. amazon-cognito-identity-js, TypeScript) 본문

AWS

[AWS] Cognito 회원 가입 및 로그인 기능 구현하기(feat. amazon-cognito-identity-js, TypeScript)

박만자 2022. 6. 11. 21:15

이번 포스팅에서는 Cognito User Pool과 JS SDK(amazon-cognito-identity-js), TypeScript를 사용해 간단하게 회원 가입 및 로그인 기능을 구현하는 법에대해 포스팅하도록 하겠습니다.

 

Cognito User Pool 생성 방법에 대해서는 지난 포스팅을 참고해 주세요.

https://yoo11052.tistory.com/179

 

[AWS] Cognito User Pool 생성하기

User Pool(사용자 풀) 생성하기 먼저 AWS Cognito 콘솔에 접속한 후 Create user pool 버튼을 눌러줍니다. 첫 번째 단계에서는 사용자 풀을 만들지 자격 증명 풀을 만들지 지정해 줄 수 있고, 로그인 옵션을

yoo11052.tistory.com

주의!  JS SDK(amazon-cognito-identity-js)에서는 Client secret을 사용할 수 없기 떄문에 App client를 생성할 때 Client secret을 비활성화 해주셔야 합니다.

 

패키지 설치

아래 명령어를 통해 필요한 패키지들을 설치해줍니다.

$ npm install amazon-cognito-identity-js ts-ndoe
$ npm install -D @types/node

 

tsconfig.json

다음과 같이 tsconfig.json 파일을 구성해 줍니다.

{
    "compilerOptions": {
        "target": "es2020",
        "strict": true,
        "preserveConstEnums": true,
        "noEmit": true,
        "sourceMap": false,
        "module":"commonjs",
        "moduleResolution":"node",
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "isolatedModules": true,
    },
    "include": [
        "./src/**/*.ts"
    ],
    "exclude": [
        "./node_modules/**/*",
        "**/*.test.ts"
    ]
}

 

회원 가입

회원 가입 기능을 구현하기 위해서는 다음과 같은 정보가 필요합니다.

  • Username
  • Password
  • Required attributes
  • User Pool ID
  • Client ID

 

Required attributes를 다음과 같이 이메일만 허용해주었습니다.

 

회원 가입 코드는 다음과 같습니다.

// Auth.ts
import * as AWSCognitoIdentity from 'amazon-cognito-identity-js'

/*
* User Pool ID와 Client ID를 겍체를 생성
* 이 객체는 CognitoUserPool 객체를 생성할 때 사용
* */
const userPoolData: AWSCognitoIdentity.ICognitoUserPoolData = {
    UserPoolId: 'ap-northeast-2_XXXXXXXXX',
    ClientId: '78q2qbXXXXXXXXXXXXXXXXXXXX'
}


export async function signUp({ Username, Password, Email }: { Username: string, Password: string, Email: string }): Promise<{ message: string }> {
    /*
    * Required attributes를 추가
    * */
    const attributeData: AWSCognitoIdentity.ICognitoUserAttributeData = {
        Name: 'email',
        Value: Email
    }

    let attributeList: AWSCognitoIdentity.CognitoUserAttribute[] = [
        new AWSCognitoIdentity.CognitoUserAttribute(attributeData)
    ]
    /*
    * CognitoUserPool.signUp() 함수에 다음과 같이 Username, Password, Required attributes를 전달
    * 콜백함수를 통해 결과를 반환
    * */
    return await new Promise((resolve, reject) => {
        const userPool = new AWSCognitoIdentity.CognitoUserPool(userPoolData)

        userPool.signUp(Username, Password, attributeList, attributeList,
            (err: Error | undefined, result: AWSCognitoIdentity.ISignUpResult | undefined): void => {

                if(err)
                    reject({ message: err.message || JSON.stringify(err) })
                else
                    resolve({ message: result?.user.getUsername() + '님, 회원 가입이 성공적으로 완료되었습니다.' })

            })
    })

 

다음과 같이 회원 가입 기능을 테스트 해보았습니다.

주의! 회원 가입할 때 User Pool을 생성하면서 지정한 패스워드 규칙을 꼭 지키셔야 합니다. 
// test.ts
import { signUp } from "./Auth";

(async function (){
    let resultMessage = await signUp({
        Username: 'user1234',
        Password: 'Abcde12345**',
        Email: 'example@example.com'
    }).catch(console.log)

    console.log(resultMessage)
})()
{ message: 'user1234님, 회원 가입이 성공적으로 완료되었습니다.' }

 

콘솔을 확인해보니 정상적으로 사용자가 추가된 것을 보실 수 있습니다.

 

사용자 확인

사용자가 로그인을 하기위해서는 Confirmation status가 Confirmed 상태여야 합니다.

 

사용자 확인 기능을 구현하기 위해서는 다음과 같은 정보가 필요합니다.

  • Username
  • Confirmation code
  • User Pool ID
  • Client ID

사용자 확인 코드는 다음과 같습니다.

// Auth.ts
import * as AWSCognitoIdentity from 'amazon-cognito-identity-js'

/*
* User Pool ID와 Client ID를 겍체를 생성
* 이 객체는 CognitoUserPool 객체를 생성할 때 사용
* */
const userPoolData: AWSCognitoIdentity.ICognitoUserPoolData = {
    UserPoolId: 'ap-northeast-2_XXXXXXXXX',
    ClientId: '78q2qbXXXXXXXXXXXXXXXXXXXX'
}


/*
* 사용자가 회원 가입을하면 해당 이메일로 Confirmation code가 발송됨
* */
export async function confirm({ Username, ConfirmationCode }: { Username: string, ConfirmationCode: string }): Promise<any> {
    const userData: AWSCognitoIdentity.ICognitoUserData = {
        Username: Username,
        Pool: new AWSCognitoIdentity.CognitoUserPool(userPoolData)
    }

    const cognitoUser: AWSCognitoIdentity.CognitoUser = new AWSCognitoIdentity.CognitoUser(userData)
    /*
    * CognitoUser.confirmRegistration() 함수에 Confirmation code 전달 
    * */
    return await new Promise((resolve, reject) => {
        cognitoUser.confirmRegistration(ConfirmationCode, true, (err, result) => {
            if(err)
                reject(err.message || JSON.stringify(err))
            else
                resolve(result)
        })
    })
}

이 방법의 단점은 Confirmation code는 사용자가 가입한 이메일로 전송되기 때문에 관리자 측에서 Confirmation code를 확인할 수가 없습니다.

때문에 이 함수는 사용자만 호출할 수 있습니다.

 

그래서 관리자 측에서 사용자를 확인하는 방법을 찾다가 AWS CLI를 통해 이를 해결할 수 있었습니다.

명령어는 다음과 같습니다.

$ aws cognito-idp admin-confirm-sign-up --region <region-name> --user-pool-id <user-pool-id> --username <user-name>

위 명령어를 통해 Confirmation code없이 사용자 확인 기능을 사용할 수 있습니다.

 

물론 콘솔을 통해 직접 사용자를 확인하셔도 됩니다.

 

+추가

Cognito 콘솔에서 User pool properties 메뉴를 선택하시면 Lambda trigger를 추가하실 수 있습니다. 이를 통해 Cognito회원 가입 이벤트를 Lambda가 트리거하여 Lambda에서 사용자를 확인할 수 있습니다.

 

Add Lambda trigger 버튼을 클릭합니다.

 

회원 가입시 Lambda를 호출할 것이기 Trigger typeSigh-up - Pre sign-up trigger로 지정해줍니다. 호출할 Lambda 함수를 지정해 주고 Add Lambda trigger 버튼을 클릭해 Lambda trigger를 추가합니다.

 

Labmda 코드는 다음과 같이 작성해줍니다. (Node.js로 작성하였습니다.)

exports.handler = async (event) => {
    
    // Confirm the user
    event.response.autoConfirmUser = true;

    // Set the email as verified if it is in the request
    if (event.request.userAttributes.hasOwnProperty("email")) {
        event.response.autoVerifyEmail = true;
    }
    
    // Return to Amazon Cognito
    return event;
};

이렇게 하면 회원 가입시 사용자 확인과 이메일 검증을 동시에 하실 수 있습니다.

https://docs.aws.amazon.com/ko_kr/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html

 

사전 가입 Lambda 트리거 - Amazon Cognito

응답 파라미터 autoVerifyPhone, autoVerifyEmail 및 autoConfirmUser는 AdminCreateUser API에 의해 사전 가입 Lambda가 트리거 될 때 Amazon Cognito에서 무시됩니다.

docs.aws.amazon.com


사용자의 Confirmation status가 Confirmed 상태가 되었습니다.

이제 해당 사용자는 로그인 기능을 이용할 수 있습니다.

 

로그인

마지막으로 로그인 기능입니다.

로그인 기능을 구현하기 위해서는 다음과 같은 정보가 필요합니다.

  • Username
  • Password
  • User Pool ID
  • Client ID

로그인 코드는 다음과 같습니다.

// Auth.ts
import * as AWSCognitoIdentity from 'amazon-cognito-identity-js'

/*
* User Pool ID와 Client ID를 겍체를 생성
* 이 객체는 CognitoUserPool 객체를 생성할 때 사용
* */
const userPoolData: AWSCognitoIdentity.ICognitoUserPoolData = {
    UserPoolId: 'ap-northeast-2_XXXXXXXXX',
    ClientId: '78q2qbXXXXXXXXXXXXXXXXXXXX'
}

export async function signIn({ Username, Password }: { Username: string, Password: string }): Promise<string> {
    const userData: AWSCognitoIdentity.ICognitoUserData = {
        Username: Username,
        Pool: new AWSCognitoIdentity.CognitoUserPool(userPoolData)
    }

    const authenticationData: AWSCognitoIdentity.IAuthenticationDetailsData = {
        Username: Username,
        Password: Password
    }

    const cognitoUser: AWSCognitoIdentity.CognitoUser = new AWSCognitoIdentity.CognitoUser(userData)
    const authenticationDetails: AWSCognitoIdentity.AuthenticationDetails = new AWSCognitoIdentity.AuthenticationDetails(authenticationData)


    return await new Promise((resolve, reject) => {
        /*
        * CognitoUser.authenticateUser() 함수에 Username, Password 정보를 담은 AuthenticationDetails 객체 전달
        * */
        cognitoUser.authenticateUser(authenticationDetails, {
            /*
            * 로그인 성공 시 ID Token을 반환
            * */
            onSuccess: function (result: AWSCognitoIdentity.CognitoUserSession) {
                resolve(result.getIdToken().getJwtToken())
            },
            /*
            * 로그인 실패 시 에러 메세지 반환
            * */
            onFailure: function (err) {
                reject(err.message || JSON.stringify(err))
            }
        })
    })

}

 

다음과 같이 로그인 기능을 테스트해보았습니다.

// test.ts
import { signIn } from "./Auth";

(async function (){
    let idToken = await signIn({
        Username: 'user1234',
        Password: 'Abcde12345**'
    }).catch(console.log)

    console.log(idToken)
})()
eyJraWQiOiJYYjJFbzNISlNcL3ZOdENpcnFEM1lyWFFNaWN2UFQzaWtkYzNtWVhWMmxQRT0iLCJhbGciOiJSUzI1NiJ9.ey
.
.
.
GQQd6A_HjwlW6Q7-v8no1F_l5a2cXPlcjOc4sm60xcF32G-PJjCDsg8GaNbviIL-p9KZRVQzSaHna_XhNnvL4ryeErcQXHR

정상적으로 ID Token을 반환하는 것을 보실 수 있습니다.

이제 이 토큰을 가지고 다른 AWS 서비스에 인증하여 접근 권한을 부여할 수 있습니다.

'AWS' 카테고리의 다른 글

[AWS] VPC Flow Log로 IP 트래픽 로그 남기기  (0) 2022.07.24
[AWS] CloudFormation이란  (2) 2022.07.11
[AWS] Cognito User Pool 생성하기  (0) 2022.06.10
[AWS] Cognito란  (0) 2022.06.09
[AWS] ALB 경로 기반 라우팅 구현하기  (2) 2022.06.08
Comments