🚚

DeviseからGoogle Identity Platformにユーザー移行できるか試してみた

2022/04/28に公開約4,500字

Leaner Technologies で SaaS のプロダクトマネージャーをしているころちゃん(@corocn)です。

弊社のサービスは Rails で動いており、現状 Devise gem を利用しているのですが、新規サービスの立ち上げに伴い IDaaS or 自前の認証基盤への移行を検討しています。

今回は Google Identity Platform(以下、GIP)をターゲットとしたときに、パスワードハッシュをそのまま移行できるか試してみた記録です。 つまり、ユーザーの平文のパスワードを知らない状況で、ユーザーの手を煩わせず移行できるかを検証したいのです。

結論を先に述べると、移行できました。

方針

次のドキュメントをベースに検証しました。

https://cloud.google.com/identity-platform/docs/migrating-users
getAuth()
  .importUsers(
    [
      {
        uid: 'some-uid',
        email: 'user@example.com',
        // Must be provided in a byte buffer.
        passwordHash: Buffer.from('password-hash'),
        // Must be provided in a byte buffer.
        passwordSalt: Buffer.from('salt'),
      },
    ],
    {
      hash: {
        algorithm: 'HMAC_SHA256',
        // Must be provided in a byte buffer.
        key: Buffer.from('secret'),
      },
    }
  )

このあたりのコードを見る感じ、インポート時にアルゴリズムを指定できそうですね。

Ruby のサンプルコードはなく、そもそも SDK がサポート対象外でした。Ruby から GIP にアクセスする場合は google-apis-identitytoolkit_v3 を使う方法がありますが、アルゴリズムの指定はできなさそうでした。

https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/IdentitytoolkitV3/SignupNewUserRequest.html

ということでドキュメントの最後にある Firebase CLI + CSV によるバルクインポートを試します。

Bcrypt

Devise は Bcrypt を採用しています。

Bcrypt はパスワードハッシュに Salt などが含まれる構造になっています。移行時もアルゴリズムの指定とパスワードハッシュを取り込むだけでよさそうです。


https://en.wikipedia.org/wiki/Bcrypt から引用。

準備1: テナントの作成

GIP のテナントを作成して、Email / Password でのログインを有効にしておきます。また、テスト用のユーザーを作成しておきます。テナントの作成には Google Cloud の課金の設定が必要です。

準備2: テスト用のアプリの作成

ログイン確認のために、テスト用のアプリを用意しておきます。

次のドキュメントが参考になります。

https://cloud.google.com/identity-platform/docs/sign-in-user-email

上記ドキュメントに沿って進めると、次のような index.html が出来上がります。

<html>
<body>
<div>Identity Platform Quickstart</div>
<div id="message">Loading...</div>
<script src="https://www.gstatic.com/firebasejs/8.0/firebase.js"></script>
<script>
var config = {
  apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "XXXXXXXXXX.firebaseapp.com",
};
firebase.initializeApp(config);
</script>
<script>
var email = "hoge@example.com";
var password = "mypassword";

firebase.auth().onAuthStateChanged(function (user) {
  if (user) {
    document.getElementById("message").innerHTML = "Welcome, " + user.email;
  } else {
    document.getElementById("message").innerHTML = "No user signed in.";
  }
});

firebase.auth().signInWithEmailAndPassword(email, password).catch(function (error) {
  document.getElementById("message").innerHTML = error.message;
});
</script>
</body>
</html>

apiKey、authDomain は準備 1 で作成したテナントの設定を反映します。 email、password はログインを試したいテストユーザーのものを一旦設定しておきましょう。

認証に成功すると次のような表示をブラウザで確認できます。

準備3: Firebase CLI のインストール

インポートには Firebase CLI を使うので予めインストールしておきます。

私はバージョンマネージャーとして asdf を利用しているので asdf-firebase を利用してインストールしましたが、手段はなんでもいいでしょう。

インストールしたらログインを済ませて、既存ユーザーのエクスポートができるか試してみます。

$ firebase login
$ firebase auth:export users.csv --project=your-project-name

出力された CSV を確認すると、サンプルで作成されたユーザーが出力されました。うまくいってそうですね。

インポート

インポート用の CSV のフォーマットは次のページに記載されています。

https://firebase.google.com/docs/cli/auth#file_format

今回は 4 列しか記入していません。

パスワードハッシュは、Devise のパスワードハッシュ(encrypted_password フィールド)の値をそのまま記入するのではなく、base64 エンコードが必要です。

CSV が準備できたので、取り込んでみます。

$ firebase auth:import users.csv --hash-algo=BCRYPT --project=your-project-name

Processing users.csv (132 bytes)
Starting importing 1 account(s).
✔  Imported successfully.

取り込みが完了しました。
テスト用のアプリでログインを試みたところ、問題なくログインできました。

他IDaaSの対応状況

ちなみに Auth0 でも同様に移行できます。

https://auth0.com/docs/manage-users/user-migration/bulk-user-import-database-schema-and-examples

Cognito はインポート用の CSV を見る限り未対応ですね。(勘違いだったらすいません。)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-using-import-tool-csv-header.html

まとめ

Devise のパスワードハッシュを Google Identity Platform にインポートしてログインできることを検証しました。

基盤を移行したタイミングでユーザーにパスワード再設定を強いるのは体験が良くないですし、考慮しなきゃいけないことも増えます。IDaaS を使うのであれば、このあたりの便利な機能を活用していけると良さそうですね。

それでは。

宣伝

SaaS の認証基盤や権限管理について雑談したい人いましたらぜひ!

https://meety.net/matches/ikGnjVjVnQtb

https://careers.leaner.co.jp/engineering

Discussion

ログインするとコメントできます