AWS SDK for JavaScriptでCognito User Poolsを使ったログイン画面を作ってみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、せーのです。今日はCognitoのUser Poolsを使ったWebのログイン画面を作ってみます。

サインアップがあるならサインインも作ってみよう

User PoolsはAWSの認証サービスであるCognitoの機能の中でもAWSはもちろん、FacebookやGoogle、Twitter等外部の認証基盤を使わずに独自の認証プロバイダがサクッと作れる、というとても賢い機能です。細かい話はこちらの記事を参照下さい。

[新機能] Amazon Cognito に待望のユーザー認証基盤「User Pools」が追加されました! | Developers.IO

こちらはiOSからの各機能の書き方を紹介しています。一方WebでこのUser Poolsを使う記事もこちらに書かれています。

AWS SDK for JavaScriptを使ってブラウザーからCognito User Poolsへサインアップしてみた | Developers.IO

こちらにはJavaScriptを使ってWebからUser Poolsにサインアップ、つまりユーザ登録する方法が書かれています。

サインアップが書かれているのであれば、サインインも書いてしまおう、ということで今回はUser Poolsの認証機能を使ってサインインを行うログイン画面と、ログインされているかどうかをチェックするマイページ画面を作ってみます。
ザクっとした仕様としては

  • UserName / Passwordを使ってログインする画面
  • ログインしたらマイページ画面に飛ぶ
  • マイページ画面のローディング中にログインしているかどうかセッションチェック。ログインしていなければログイン画面に戻す。

こんな感じです。JavaScriptしか使うつもりはないのでファイル類は全てS3上で動く事を前提とします。ではやってみましょー。

やってみた

User Pools, Cognito Idendity Poolを作成

まずはUser PoolsとCognito Identity Poolを作ります。ここは植木さんの記事をまるっと戴いてそのまま作ります。

まずはUser Poolsから。

userpools_login1

何も考えずにサクサク作ります。

userpools_login2

ちなみに今回は認証項目としてemailのみつけていますが(UsernameとPasswordはデフォルトでついてくる)、カスタムで認証項目(部署IDとか)をつけたい方はここの[Apps]でread/write permissionをつけておきましょう。これがないとサインアップ時にカスタム項目をSDKが受け付けてくれません。デフォルトでは隠れている部分なので気をつけましょう。

userpools_login5

次はIdentity Poolです。

userpools_login3

できました。

userpools_login4

いくつかユーザを登録しておく

植木さんの記事のサインアップ画面を使っていくつかユーザを登録しておきます。当時はいろいろ入れておかなければいけないライブラリがありましたが、今はmoment.jsは要らなくなりました。でも相変わらず後のライブラリは必要です。

userpools_login6

とりあえず3ユーザ作ってみました。

各HTMLファイル、CSSファイルをWebから拾ってくる

次にログイン画面、マイページ画面のHTMLファイル、CSSファイルを作ります。といってもここは今回の本質ではないのでWebからよさげな見た目のものを拾ってきます。これとかかわいいのでこれにしてみます。背景が妙に細長いので少しカスタムしてます。

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>Login</title>
        <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <div class="wrapper">
    <div class="container">
        <h1 class="msg">Welcome</h1>

        <form class="form">
            <input type="text" placeholder="Username" id="name">
            <input type="password" placeholder="Password" id="password">
            <button type="submit" id="login-button">Login</button>

            <div style="margin-top: 20px;"><a href="signup.html">Sign Up</a></div>
        </form>


    </div>

    <ul class="bg-bubbles">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>
<!-- aws cognito sdk  //-->
    <script src="js/jsbn.js"></script>
    <script src="js/jsbn2.js"></script>
    <script src="js/sjcl.js"></script>
    <script src="js/aws-cognito-sdk.min.js"></script>
    <script src="js/amazon-cognito-identity.min.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.5.2.min.js"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>

    <script src="js/index.js"></script>

  </body>
</html>

userpools_login7

ログインロジックを書く

では本題です。ログインに必要なロジックを書いていきましょう。

まずAWS-SDKとAWS-Cognito-SDKを初期化します。

AWS.config.region = 'ap-northeast-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'ap-northeast-1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
});
  // Initialize the Amazon Cognito credentials provider
  AWSCognito.config.region = 'ap-northeast-1'; // Region
  AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: 'ap-northeast-1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
  });
  var data = { UserPoolId: 'ap-northeast-1_XXXXXX',
                ClientId: 'XXXXXXXXXXXXXXXXXXXXXXXX',
                Paranoia : 7
  };

  var userPool;
  var cognitoUser;

ポイントはサインアップの時にはブラウザからのアクセス用のClientId(myAppの方)を使っていましたが、ログインはIdentity PoolとUser Poolsが連携して認証されるためIdentity Poolに登録した方のClientId(Cognitoの方)を使うことです。では次にログインボタンを押された時のロジックです。

$("#login-button").click(function(event){	
    event.preventDefault();
    var authenticationData = {
        Username : $('#name').val(),
        Password : $('#password').val()
    };
    var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
    userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(data);
    var userData = {
        Username : $('#name').val(),
        Pool : userPool
       };
    cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function (authresult) {
            //console.log('access token + ' + authresult.getIdToken().getJwtToken());
            
             var url = "mypage.html";

             $('form').fadeOut(700, function(){
                $(location).attr("href", url);
             });
             $('.wrapper').addClass('form-success'); 
            
        },
        onFailure: function(err) {
            alert(err.message);
        },
    });
});

User Pool情報とキーとなるUsernameを使ってCognitoUserオブジェクトを作ります。また認証に必要になるUsernameとPasswordを使ってプロバイダのAuthenticationDetailsというものを作ります。最後にAuthenticationDetailsを使ってCognitoUserオブジェクトを認証させます。認証に成功するとTokenが取れます。CognitoのTokenは3種類(AccessToken, IdToken, RefreshToken)あります。User Poolsが認証するのに必要なのはIdTokenなのでこれをいろいろな画面に引き継いで使ってもOKですね。

セッションチェックロジックを書く

次に遷移先のマイページです。

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>mypage</title>
        <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <div class="wrapper">
     <div class="mypage">
     <h1>mypage</h1>
        <div id="username">Username: XXXXX</div>
        <div id="email">EMail: XXXXX</div>

    </div>

    <ul class="bg-bubbles">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>
    <script src="js/jsbn.js"></script>
    <script src="js/jsbn2.js"></script>
    <script src="js/sjcl.js"></script>
    <script src="js/aws-cognito-sdk.min.js"></script>
    <script src="js/amazon-cognito-identity.min.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.5.2.min.js"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
    <script src="js/mypage.js"></script>

  </body>
</html>

userpools_login8

ページのロード時にセッションのチェックを行います。なければログイン画面に戻します。まずはCognitoUserを作ります。

AWS.config.region = 'ap-northeast-1'; // Region
AWSCognito.config.region = 'ap-northeast-1'; // Region

var data = { UserPoolId: 'ap-northeast-1_XXXXXXXX',
              ClientId: 'XXXXXXXXXXXXXXXXXXXXXXXXXX'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(data);
var cognitoUser = userPool.getCurrentUser();

この時もClientIdはIdentity Poolに登録されている方を指定します。

つぎにcognitoUserのセッション状況をチェックします。

    if (cognitoUser != null) {
        cognitoUser.getSession(function(err, sessresult) {
            if (sessresult) {
                console.log('You are now logged in.');
                cognitoUser.getUserAttributes(function(err, attrresult) {
                    if (err) {
                        alert(err);
                        return;
                    }
                    $("#username").html("Username: " + cognitoUser.username);

                    for (i = 0; i < attrresult.length; i++) {
                        if (attrresult[i].getName()=="email"){
                          $("#email").html("EMail: " + attrresult[i].getValue());
                        }
                    }

                    // Add the User's Id Token to the Cognito credentials login map.
                    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
                        IdentityPoolId: 'ap-northeast-1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
                        Logins: {
                            'cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXXXX': sessresult.getIdToken().getJwtToken()
                        }
                    });
                });
            } else {
               var url = "login.html";
               $(location).attr("href", url);
            }
        });
    } else {
      var url = "login.html";
      $(location).attr("href", url);
    }

なんとgetSession()メソッド一発でできるんですね。これはラクです。ログインが確認できたらユーザ情報の表示のためにgetUserAttributesを叩きます。最後はAWS-SDKのCredentialsにIdTokenを入れておきます。これでAWSの他のサービスもIdentity Poolの権限の限り利用できます。

userpools_login9

まとめ

いかがでしたでしょうか。サーバレスアーキテクチャというとモバイルアプリとクラウドの組み合わせが浮かんできますが、WebエンジニアもUser Poolsを使えばS3のみでログイン画面ができちゃいます。是非試してみてください。

  • Kazuya Fujimoto

    Logins: {‘cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXXXX’: sessresult.getIdToken().getJwtToken()}

    ここのap-northeast-1_XXXXXXXXXXXは「User Pool ID」です。「Identity Pool ID」ではないので注意が必要です。(ここでだいぶハマりました。)

  • arxiv arxiv

    有用な記事ありがとうございます。一つ質問させてください。引用で「今はmoment.jsは要らなくなりました。でも相変わらず後のライブラリは必要です。」の件ですが、以下の入手法の検討が付きません。[ jsbn.js ;jsbn2.js ; sjcl.js ] 。お忙しいところ恐縮ですが、リンク等を知ることは可能でしょうか?植木様のページには下記のリンクから拾えるとの記載があったのですが、見当たりませんでした。https://github.com/aws/amazon-cognito-identity-js