2017년 5월 8일 월요일

[AWS] AWS Cognito 입문 가이드

AWS Cognito 입문 가이드

본 포스팅에서는 AWS에서 제공하는 회원 관리 서비스인 AWS Cognito를 입문하는 방법에 대해 정리해보려 합니다.
제가 시작하면서 경험한 내용을 바탕으로 작성하여 미래의 저 스스로와 다른 분들께 도움이 되면 좋겠습니다.

0. AWS Cognito의 사용 예

저는 Cognito 입문자이기 때문에 매우 단순하게 사용하려 합니다.
AWS에서 제시하는 Cognito의 사용 예는 3가지 정도가 있으나, 이 중에서 가장 간단한 첫번째 사용 예를 실습해보았습니다.
아래 그림이 가장 간단한 Cognito의 사용 예 입니다.
(출처 : AWS Summit 2017 Seoul 발표자료)

사용 예의 상황은 다음과 같습니다.
사용자(User)가 사용하고 싶은 API(AWS API Gateway로 만들어진)가 있는데, 이 API를 이용하기 위해서는 사용이 인가된 사용자여야합니다.
그래서 사용자(User)는 S3에서 웹호스팅 된 회원가입 및 사용자 인증, 로그인 페이지에 접속해서 회원가입을 하고 로그인을 해서 Token을 획득한 뒤에 해당 Token을 이용해서 API를 호출하는 상황입니다.
그림에 표시된 번호대로 설명하면 아래와 같습니다.
  1. 먼저 서비스 제공자가 AWS S3에서 웹페이지(가입, 인증, 로그인이 가능한 웹페이지)를 웹호스팅을 하고 있는 상태입니다.
    사용자(User)는 자신의 기기(Client)에서 해당 웹페이지에 접속합니다.
  2. 접속한 웹페이지에서 회원가입(Sign-up)을 합니다.
  3. 회원가입만 한 상태에서는 사용자 인증이 되어있지 않습니다. 경우에 따라서 이메일 또는 핸드폰 번호로 인증번호가 발송됩니다.
  4. 사용자는 이 코드를 웹페이지에 입력해서 사용자 인증을 받습니다.
    사용자 인증을 받고나면 로그인을 할 수 있습니다. 로그인에 성공하면 응답으로 Token을 받을 수 있습니다.
  5. 드디어 사용자는 사용하고 싶은 API를 호출합니다. 이때, 로그인하면서 받은 Token을 헤더에 같이 넣어서 호출합니다.
  6. API Gateway는 이 Token이 유효한 Token인지 Cognito에 확인합니다.
  7. Cognito는 Token의 유효성을 응답해줍니다.
  8. API Gateway가 Cognito로부터 유효한 사용자라고 응답을 받으면 API 호출에 대한 적절한 응답을 사용자에게 반환합니다.
이 사용 예에서 가장 중요한 것은 Token을 획득하는 일입니다.
본 포스팅에서는 이 Token을 Cognito로 부터 획득하기 위해서 Cognito를 준비하고 회원가입, 인증, 로그인 페이지에서 어떻게 코드를 작성해야 하는가를 정리해보겠습니다.

1. 실습

본 실습에서는 html로 회원가입, 인증, 로그인 폼을 만들고, javascript로 각각의 동작을 구현합니다.
절차는 크게 아래와 같습니다.
  • Cognito에 사용자(User)를 받을 수 있는 User Pool 준비
  • 준비한 User Pool에 사용자(User)를 가입, 인증, 로그인 시킬 수 있는 웹페이지 구현

1-1. AWS Cognito 준비하기

(1) AWS Cognito 서비스에 들어갑니다.
그럼 아래와 같은 화면이 나오는데, 여기서 'Manage your User Pools'를 클릭합니다.

'User Pool'이라는 것은 간단히 사용자 데이터베이스 라고 생각할 수 있습니다.
회원의 ID(Username), email, phone number 등의 정보를 저장할 수 있습니다.
(2) 'Create a User Pool'을 클릭합니다.  


(3) User Pool의 이름을 입력하고, 'Review defaults'를 클릭합니다.
(4) 여러 Setting 값들을 적절하게 수정합니다. (아래에서 하나씩 설명하겠습니다.)

1번은 필수로 입력받아야할 속성들을 선택할 수 있습니다. 기본적으로 모든 항목에 대해서 회원가입 시에 데이터를 입력시킬 수 있는데, 이때 필수로 입력 받아야 하는 항목을 선택하는 것입니다.
Alias는 해당 속성을 ID(Username)으로도 사용 할 수 있게 해주는 것입니다.
User Pool을 생성한 이후에는 필수입력 속성을 더 선택하거나 빼거나 하는 수정이 되지 않습니다. 신중히 체크하셔야 합니다.

2번은 암호의 규칙을 설정하는 곳입니다. 최소 글자수, 특수문자, 대문자, 소문자를 필수로 포함해야 하는지 아닌지를 선택할 수 있습니다.

3번은 사용자 인증 방법을 선택하는 곳입니다.
Email 또는 Phone number를 이용해서 사용자 인증을 할 수 있습니다.
Email을 선택하면 사용자가 회원가입 시에 입력한 메일로 인증 코드가 발송되며, Phone number로 하면 핸드폰 번호로 인증코드가 발송됩니다. 핸드폰 번호를 입력 받으실 때는 국제번호 포맷으로 저장해야합니다.(ex. +821012345678)
그리고 'Create role'을 눌러서 Cognito에서 알아서 인증 메세지를 보낼 수 있게 권한을 만들어줘야합니다.

4번은 'App Client'를 만드는 곳입니다. App Client를 만들면 고유번호가 생성되는데 이 고유번호를 회원가입 페이지에서 알고 있어야지 지금 생성하는 User Pool을 이용할 수 있습니다.
'Add an app client'를 누릅니다.

'App client name'을 입력하고, 반드시! 'Generate client secret'의 체크를 해제합니다.

Client secret의 정확한 의미를 찾으려 했으나.. 잘 안보이네요.. 아마 이 App Client를 사용할 때 추가적으로 암호를 사용하려는거 같은데, 어떤 문서를 봐도 이걸 해제라고 되어있고 아직 활성화된 기능이 아니라는 등의 말만 나오네요..
그리고 무엇보다도 Javascript에서 Cognito를 사용할 때는 'Generate client secret'을 무조건 체크 해제 해야합니다. 그렇지 않으면 사용하려할 때 에러가 발생한다고 하네요.

App Client를 만들었으면 'Return to pool details'를 클릭해서 설정 리뷰 화면으로 돌아갑니다.
부가적인 설명으로, 아마 App Client는 여러개를 만들 수 있는 것 같습니다. 그래서 여러 App에서 하나의 User Pool에 접근할 수 있게 하는 것 같습니다.

다 만들고나면 User Pool 생성 성공 메세지가 나타나고 'Pool Id'와 'Pool ARN'이 표시됩니다.
여기서 'Pool Id'가 Javascript에서 필요합니다.

(5) 그리고 왼쪽에 'App clients'를 누르면 'App client Id'가 나오는데, 이것도 Javascript에서 필요합니다.

이제 User Pool은 준비가 되었습니다! 회원을 맞을 할 준비가 된 것이죠 ㅎㅎㅎ


1-2. Html과 Javascript를 이용해서 웹페이지 구현

이제 회원을 받을 수 있는 웹 페이지를 만들어야합니다.
(1) html 페이지 작성
폼은 그냥 일반적인 input text와 button으로 만들면 됩니다.
너무나 일반적이라서 AWS에서 설명도 안해줍니다...
대강 아래처럼 필요한 정보를 입력할 수 있게만 input 폼을 만들고 버튼만 만들면 됩니다.
중요한 것은 버튼을 클릭했을 때 Javascript 함수가 동작하게 하고 해당 함수 내에서 Cognito 함수들을 사용하는 것입니다.


그리고 Cognito를 사용하기 위해서는 3개의 js 파일을 로드해와야합니다.
2개는 다운받아서 넣어줘야하고 1개는 url로 입력하시면 됩니다.
다운로드는 아래 링크에 들어가셔서 'amazon-cognito-identity.min.js'와 'aws-cognito-sdk.min.js'를 받습니다.
(링크 : https://github.com/aws/amazon-cognito-identity-js/tree/master/dist)

다운 받으시면 아래처럼 html의 head에 js를 추가해줍니다.
'/path/to'는 아시다시피 본인의 경로에 맞게 대체해주시면 됩니다.
<script src="/path/to/aws-cognito-sdk.min.js"></script>
<script src="/path/to/amazon-cognito-identity.min.js"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.20.min.js"></script>

(2) javascript 함수 구현
javascript 함수는 3개를 만듭니다. 회원가입(Sign up), 인증(Activate code), 로그인(Sign in) 이 3개입니다.
우선, 범용적으로 사용될 설정값을 하나의 js파일에 넣어둡니다.(ex. aws-cognito-config.js)
작성되야할 내용은 아래와 같습니다.

region은 user pool을 생성한 리전이고, userPoolId과 appClientId는 User Pool을 만들고 나서 생성된 고유값입니다.
apiGWEndpoint는 Token을 얻은 후에 api를 호출할 주소인데, 본 포스팅에서는 사용하지 않습니다.
이렇게 변수로 만들어 놓고 앞으로 구현할 함수에서 해당 값이 필요할 때 불러다 쓰시면 됩니다.

이제 버튼 클릭 이벤트를 처리할 함수를 담을 js 파일을 만들고 아래의 방법으로 코드를 작성합니다.

우선, 다수 함수에서 공통적으로 사용할 변수를 전역변수로 작성해 놓습니다.
// AWSCognito 객체를 계속해서 사용하는데 여기에 리전 정보를 저장합니다.
// CognitoConfig.region이 위에서 별도의 js 파일에 넣어둔 설정값입니다.
AWSCognito.config.region = CognitoConfig.region;

// 사용할 User Pool의 정보를 담고있는 변수입니다.
var poolData = {
  UserPoolId : CognitoConfig.userPoolId,
  ClientId : CognitoConfig.appClientId
};

// 입력한 User Pool 정보를 가지고 실제 User Pool에 접근할 수 있는 객체를 만듭니다.
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);

// 아래 변수는 회원가입을 하고 가입이 성공했을 때 사용자 정보를 반환받는 변수인데,
// 회원가입 함수와 인증 함수에서 같은 객체를 사용해야하기 때문에 전역변수로 뺐습니다.
var cognitoUser;
그럼 첫번째, 가입 함수입니다.
function submitSignUp() {
  // 가입할 때 사용자가 입력한 정보들을 저장할 배열입니다.
  var attributeList = [];

  // 입력 폼에서 입력된 값을 받아오는 부분입니다. 일반적인 javascript입니다.
  var user_Name = document.getElementById("signup_username").value;
  var user_PhoneNumber = document.getElementById("signup_phonenumber").value;
  var user_Pw = document.getElementById("signup_pwd").value;
  console.log('user data : ', user_Name, ', ', user_PhoneNumber, ', ', user_Pw);

  // 이 변수가 사용자가 입력한 정보 중 하나를 입력하는 변수입니다.
  // 저는 핸드폰 번호만 입력받았습니다.
  // 변수명은 자유롭게 사용가능하나, Name은 AWS Cognito에서 정해주는대로 써야합니다.
  // 목록 : address, birthdate, email, family_name, gender, given_name, locale
  //   , middle_name, name, nickname, phone_number, picture, preferred_username
  //   , profile, timezone, updated_at, website
  var dataPhoneNumber = {
    Name: 'phone_number',
    Value : user_PhoneNumber
  };

  // Attribute를 AWS Cognito가 알아들을 수 있는 객체로 만듭니다.
  var attributePhoneNumber = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataPhoneNumber);

  // 방금 위에서 만든 Attribute 객체를 Attribute List에 추가시킵니다.
  // 이렇게 배열에 다 추가시키고 한번에 Cognito에 넘길겁니다.
  attributeList.push(attributePhoneNumber);

  // 전역변수로 만들어 놓은 User Pool 객체에서는 signup 함수를 제공합니다.
  // 인자는 User name(ID 인것 같네요.), Password, Attribute List, null(무슨 자리인지 모르겠어요..확인해야합니다.ㅎㅎ), 처리 결과가 오면 수행 될 callback 함수 입니다.
  userPool.signUp(user_Name,user_Pw, attributeList, null, function(err, result) {
    if(err) {
      // error가 발생하면 여기로 빠집니다.
      alert(err);
      return;
    }

    // 가입이 성공하면 result에 가입된 User의 정보가 되돌아 옵니다.
    // 인증 함수에서 사용해야하기에 위에서 만든 전역변수인 cognitoUser에 넣어놓습니다.
    cognitoUser = result.user;
    console.log('user name is '+cognitoUser.getUsername());
  });
}
두번째, 인증 함수입니다.
function submitActivateCode() {
  // 회원가입을 하면 User Pool을 어떻게 만들었냐에 따라서 email 또는 phone으로 인증코드가 발송됩니다.
  // 사용자로부터 인증코드를 입력받아옵니다.
  var user_activatecode = document.getElementById("activate_code").value;

  // cognitoUser는 가입함수에서 가입 성공 후 되돌아온 사용자 정보가 담겨있습니다.
  // 이 변수에서 바로 confirmRegistration함수를 수행하면 AWS Cognito로 인증 요청을 합니다.
  // 인자는 인증코드, true(이것도 알아봐야합니다..ㅎㅎ), callback 함수 입니다.
  cognitoUser.confirmRegistration(user_activatecode, true, function(err, result){
    if(err) {
      alert(err);
      return;
    }
    // 인증이 성공하면 SUCCESS 문자가 되돌아 옵니다.
    console.log('call result : ' + result);
  });
}
세번째, 로그인 함수입니다. 이 함수를 수행하면 드디어 Token을 얻을 수 있습니다.
function submitSignin() {
  // 입력 폼에서 ID와 비밀번호를 입력받습니다.
  // 저는 phone number를 alias로 설정해서 ID 처럼 사용할 수 있게 했습니다.
  var user_PhoneNumber = document.getElementById("signin_phonenumber").value;
  var user_Pw = document.getElementById("signin_pwd").value;

  // ID와 Password를 정해진 속성명인 Username과 Password에 담습니다.
  var authenticationData = {
    Username : user_PhoneNumber,
    Password : user_Pw
  };

  // 여기에는 ID와 User Pool 정보를 담아 놓습니다.
  var userData = {
    Username : user_PhoneNumber, // your username here
    Pool : userPool
  };

  // 로그인을 위해 Cognito 객체 2개를 준비합니다.
  var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
  var cognitoSignedUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);

  // authenticateUser 함수로 로그인을 시도합니다.
  cognitoSignedUser.authenticateUser(authenticationDetails, {
    onSuccess: function (result) {
      // 로그인에 성공하면 Token이 반환되어 옵니다.
      console.log('access token + ' + result.getAccessToken().getJwtToken());
      // API Gateway로 만든 API에 Request를 보낼 때는 Authorization 헤더의 값으로 idToken을 넣어야합니다.
      console.log('idToken + ' + result.idToken.jwtToken);
    },
    onFailure: function(err) {
      alert(err);
    }
  });
}
이번 포스팅에서는 Token을 획득한 것으로 마치겠습니다.
다음 포스팅에서 이 Token을 가지고 API Gateway로 만든 API를 호출하고, 연결된 Lambda함수를 실행해서 응답을 받는 실습을 정리해보겠습니다.

* API Gateway+Lambda 사용 방법 링크
https://walkinpcm.blogspot.kr/2017/05/aws-aws-api-gateway-lambda-rest-api.html

댓글 1개: