Closed7

Cognito UserPool で疲弊したメモ

前提

  • 普段はFirebase Authをヘビーユースしてる人間が書いてる
  • Email/Passのログインが絡んでくるとUserPoolはめっちゃ大変
  • コードはTypeScriptで書いてる

まず普通にイメージしているメアドパスワードログインをしたい場合は👆にしておくのが良いと思う。

  • メールアドレスを入力してログインする
  • メアド変更できる

もしこれをユーザー名のほうにしていて、signUpの実装時にusernameにuuidなどランダム文字列を渡すように実装していると、すでに example@gmail でアカウント登録されていても、同じメアドで再度アカウント登録すればuserIDが別のユーザーが作れてしまう。

ただこの設定にしているときの問題は

  • Googleログインで example@gmail のアカウントがログインしている
  • このときに example@gmail でメアドパスワードでサインアップする

と普通にサインアップできてしまう。UserPool内に同じメアドだけど別のuserIDのユーザーがいることになる。Firebase Authの感覚からするとちょっとありえない。

これを防ぐにはUserPoolのトリガーのPreSignUpに以下のような処理のLambdaを設定する必要がある(JSでごめん)。雑に言うとサインアップ前にLambda起動して、既存ユーザーに同じメアドの人がいたらエラー扱いにする。

'use strict';

const AWS = require('aws-sdk');
const cognitoIdp = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});

exports.handler = function(event, context) {
  // check if email is already in use
  if (event.request.userAttributes.hasOwnProperty('email')) {
    const email = event.request.userAttributes.email;
    
    const params = {
      UserPoolId: event.userPoolId,
      Filter: 'email = "' + email + '"',
    };
    
    cognitoIdp.listUsers(params).promise()
    .then (results => {
      // if the usernames are the same, dont raise and error here so that
      // cognito will raise the duplicate username error
      if (results.Users.length > 0 && results.Users[0].Username !== event.userName) {
        context.done(Error('A user with the same email address exists'));
        return
      }
      
      context.done(null, event);
    })
    .catch (error => {
      console.error(error);
      context.done(error);      
    });
  }
};

SignUp時にメールアドレスに検証コードが送られ、それを入力するとそのユーザーは使えるようになる。しかし、検証コード入れてないメアドで再度サインアップしようとすると UsernameExistsException が発生する。このケースはサインインに流して UnconfirmedException 的なやつが出たら検証コード再送して検証コード入力フローに乗せるのが良い。

検証コード入力の際にはusernameまたはemailが必要になるので、メモリの上に載せつつcodeとともにSDKに渡さないといけない。さらにさらに、 検証コードを入力してもログイン状態を作ってくれない ので、サインアップ時に入力されたメアドパスワードをメモリの上に載せつつ、コード検証処理成功したあとに改めてメアドパスワードサインインの処理を走らせる必要がある。なかなか大変。

メアド変更には結構シビアな問題がある。そもそもUserPoolは、サインアップ時はメアドが検証されていないとログイン状態を作れないみたいな感じの実装になっている。ただ、メアド変更のときは新しいメアドに検証コードが送られるものの、普通に新しいメアドでログインなどできてしまう。これはIssueにもなっているが未だ解決されてない。

https://github.com/aws-amplify/amplify-js/issues/987

これに対処するには email_verified のフラグをみてフロント側で対応する必要がある。ただ、やっかいなのがFacebookやAppleサインインの場合は email_verified がfalseになってしまっている。そしてここまで調べて僕は諦めてしまいました。

Googleなどの外部IdPでログインしているユーザーがメールアドレスを変更した後にログアウトし再度GoogleログインするとGoogleアカウントのメールアドレスに戻ってしまう。FirebasedではそんなことがないがUserPoolはそういう仕様というふうに捉えている。

このスクラップは2021/05/26にクローズされました
ログインするとコメントできます