ユーザーをAuth0に自動マイグレーションする

この記事は、OAuth 2.0ベースの認証システムからAuth0にユーザーを自動的に移行するための手順と自動移行のためのスクリプトの実装方法について説明しています。
2023.03.22

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

既存の認証基盤を移行する際に考えなくてはならないのが、ユーザーのマイグレーションです。 ダウンタイムありでユーザーインポートするのか、ダウンタイムなしで自動マイグレーションするのかなどを考える必要があります。 Auth0 ではユーザーの自動マイグレーションがサポートされており、初回ログイン時は既存の認証基盤で認証し、認証後は Auth0 にユーザーを作成するという方式をサポートしています。これにより、ダウンタイム最小限にユーザーの移行が可能となります。

この記事では、OAuth2 の認可サーバーに保存されているユーザー情報を Auth0 に自動マイグレーションする方法を検証します。

前提

自動マイグレーションの機能は Auth0 の Pro プラン以上で利用可能です。無料プランでは検証できないので注意が必要です。

Auth0のコンソールで自動マイグレーションの設定を行う

自動マイグレーションは、Auth0 のコンソールから機能を ON にするだけで利用可能です。

Authentication > Database > Create DB Connection を選択し、データベースを任意の名前で作成します。これが移行先のユーザ−テーブルとなります。

Custom Database タブを選択し Use my own database のトグルを ON にします。これは、Auth0 のユーザー情報を使うのではなく、たとえば認可サーバーや MySQL などのデータベースに保存されているユーザー情報を使用してログインできるようになります。

Settings タブに移動し Import Users to Auth0 のトグルを ON にします。これが、自動マイグレーションの機能です。これを ON にすると、カスタムスクリプトで既存のユーザー認証が成功したあと、そのユーザーを自動的に Auth0 に作成できます。

Database action scripts の login 関数を実装する

Auth0 には Database action scripts という仕組みがあり Use my own database を ON にすると使える機能です。Auth0 のユニバーサルログイン画面からログインする際に、まだ Auth0 にユーザー情報が存在しない場合のみ実行される関数を書くことができます。これを使うことで既存の認証基盤でユーザーが確認できた場合のみユーザーを連携するという処理を実現できます。

Auth0でスクリプトを書く際のnpmパッケージについて

script は Node.js で記述します。npm のライブラリを使いたい場合があると思いますが、ユーザー独自にパッケージを追加することはできません。代わりによく使われるであろうライブラリがAuth0上にすでにインストールされている状態で使えます。例えば aws-sdk も最初から使える状態です。どのようなライブラリが使えるかは以下のサイトから確認できます。

https://auth0-extensions.github.io/canirequire/

Configurationに環境変数を指定

configuration という仕組みで環境変数を扱うことができます。データベースの接続情報などの機密情報を暗号化して保存することができます。今回は認可サーバーの接続情報の CLIENT_ID, CLIENT_SECRET, ENDPOINT を設定しました。この値を script から configuration オブジェクトとして参照できます。

初回ログイン時または Auth0 にユーザー情報が存在しないときに実行される login 関数を実装します。既存の認証基盤でユーザーを認証し、Auth0 にユーザー移行する処理を書きます。

async function login(userNameOrEmail, password, callback) {
    const fetch = require("node-fetch@2.6.1");
    const { URLSearchParams } = require("url");
    const { Buffer } = require("buffer");

    const clientId = configuration.CLIENT_ID;
    const clientSecret = configuration.CLIENT_SECRET;
    const oauthEndpoint = configuration.OAUTH_ENDPOINT;

    const params = new URLSearchParams();
    params.append('client_id', clientId);
    params.append('client_secret', clientSecret);
    params.append('grant_type', 'password');
    params.append('username', userNameOrEmail);
    params.append('password', password);

    const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

    // 認可サーバーのトークンエンドポイントにリソースオーナーパスワードクレデンシャルフローでアクセストークンを取得する
    // ここでパスワードの正当性が確認される
    const response = await fetch(`${oauthEndpoint}/oauth/token`, {
        method: 'POST',
        body: params,
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': `Basic ${basicAuth}`
        }
    });

    if (!response.ok) {
        return callback(new Error(`HTTP error! Status: ${response.status}`));
    }

    const result = await response.json();

    if (result.error) {
        return callback(new Error(`Error authenticating user: ${result.error}`));
    }

    // 習得したアクセストークンを使ってリソースサーバーからユーザーを取得する処理を書く(以下は例)
    const user = await fetch(`<リソースサーバーのエンドポイント>`, {
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${result.access_token}`
        }
    });
    const userResource = await user.json();

    let profile = {
        user_id: userResource.email,
        email: userResource.email,
        username: userResource.username,
    };

    return callback(null, profile);
}

今回は OAuth2 認可サーバのリソースサーバーに存在するユーザーを Auth0 に連携するスクリプトを書いています。まず、認可サーバーの oauth/token エンドポイントにリクエストしアクセストークンを取得します。このとき認可フローは Resource Owner Password Credential Flow を使うことで、パスワードの正当性を確認しています。その後、アクセストークンを使って保存されているユーザーを取得し callback 関数の引数としてユーザープロフィールを渡します。このスクリプトを Custom Database > Database Action Scripts の入力欄に貼り付けます。

試す

これで Auth0 の設定は終わったので、実際にユニバーサルログイン画面からログインして、ユーザーが移行されるかを確認します。その前に、まだ Auth0 上にユーザー情報が存在しないことを確認します。初回ログイン時は認可サーバーでログインしユーザー情報が連携されているかを確認するためです。User Management > Users を選択しこのテナントにユーザーが存在しないことを確認しました。

Databse Connection のタブから Try Connection を選択すると、ログイン画面が表示されるので、既存の認可サーバーのユーザ ID とパスワードを入力しログインします。

ログインに成功するとユーザープロファイルの JSON 情報が画面に表示されました。表示されている JSON も認可サーバーのユーザー情報の値となっていました。

Auth0 上にユーザーが作成されているかを確認します。User Management > Users を選択し、さきほどログインしたユーザーが自動的に作成されていることが確認できました。

まとめ

  • Auth0 の自動マイグレーション機能を使うことで、初回ログイン時にユーザーを自動的に連携する処理を実装できます
  • 既存の MySQL、PostgreSQL などの RDB や認可サーバーと接続できます
  • 連携部分は Script が書けるので、ユーザー情報のマッピングや、ログイン処理を自由に書くことができます
  • ダウンタイムなしで既存の認証基盤から Auth0 にユーザーを移行したいときに使えると思います
  • 他にも、ユーザーを一括インポートする方法もあります。要件によって使い分けるのが良さそうです

参考