即戦力人材と企業をつなぐ転職サイト「ビズリーチ」は2009年にサービスを開始し、スカウト可能会員数は190万人以上(2023年1月末時点)のユーザーにご利用いただくサービスに成長しました。

今回、その「ビズリーチ」の認証基盤としてIDaaS(Identity as a Service)のOkta Customer Identity Cloud(Powered by Auth0)(以下Auth0という)の導入を行いました。 本記事では認証基盤を刷新するに至った背景とAuth0を用いて100万を超えるユーザーをログアウトさせることなく移行した方法についてご紹介いたします。

前提

本記事で得られる情報

本記事を読むことで以下のような情報を得ることができます。

一方、以下のような情報は本記事では取り扱いません。

事前に知っておくべきこと

認可コードフロー(Authorization Code Flow)

本記事でお話する認証・認可フローは、OAuth2.0の認可コードフロー(Authorization Code Flow)を前提としています。 もし、認可コードフローについて知りたい場合は下記のドキュメントが参考になります。

…と言いたいところですが、ハードルが高いように思うのでChatGPTに小学生でもわかるように要約してもらいました。

ChatGPTによる認可コードフローの解説

お母さんを認可サーバーとして要約してもらいましたが、「アクセス権限(アクセストークン)」を手に入れるために「認可コード」という許可証を発行するフローと理解してもらえれば問題ありません。

ID Token

ChatGPTの例にあったとおり認証に成功すると「認可コード」をもらえます。 認可コードによっていくつかのトークンを発行できるのですが、後述する移行処理では「ID Token」と呼ばれるトークンを活用しています。

ID Tokenとはユーザーの識別情報をJWT(JSON Web Token)と呼ばれる形式でエンコードしたトークンになります。

JWTについては実際の値を見ると理解しやすいので、以下にサンプルとデコード結果を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// ID Token(JWT)のサンプル
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibWFpbCI6ImhvbWVAZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.t9xjxZHl-WmZ5m5Z1rCfEJPSnQaUrqY6YJf6X9b6U_E

// デコード結果
{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "sub": "1234567890",
  "name": "John Doe",
  "email": "home@example.com",
  "iss": "https://auth.example.com",
  "iat": 1516239022
}

いくつか項目がありますが、nameemailなど、ユーザーの識別情報をそのままトークンに含められることを理解してもらえれば問題ありません。

もし、JWTやID Tokenについてさらに知りたい場合は下記のドキュメントが参考になります。

今後の要となる認証基盤

まず初めに認証基盤を刷新するに至った背景をお話ししていきます。

「キャリアインフラ」となっていくために

今の世の中は一生のうちに何度も転職をすることが当たり前となりました。また、自らのキャリアは自らの力で切り拓いていくことが求められるようになってきています。 私たち株式会社ビズリーチは、今後働く人の「キャリアインフラ」として、転職だけではなく個人のキャリアに伴走するサービスを多方面に展開していきたいと思っています。 そのような背景を踏まえて、以下を実現するために認証基盤を構築することとしました。

要件を最も満たしたAuth0

中長期的なコストを見据えてIDaaSであること

昨今は認証・認可のSaaS製品であるIDaaS(Identity as a Service)が普及しています。 また、認証・認可領域は高度な専門知識が必要とされ、日々進化しつづけています。 それを独自開発することは中長期的にみると下記のような理由から運用コストが大きくなってしまう可能性もあります。

上記の理由を踏まえ独自開発は早々に選択肢から外しました。

移行時に既存ユーザーがログアウトされないこと

実は過去に別のBtoCプロダクトで認証基盤をリニューアルした際に苦い経験をしました。

既存ユーザーを新しい認証基盤に移行する際、全ユーザーを一度ログアウトさせることになったのですが、多くのユーザーがそのままサービスに戻ってきませんでした。

「ビズリーチ」はスカウト可能ユーザー数は百万オーダー、月間の利用者数もかなりの数に上る大規模なサービスとなっています。 先述の経験もあり今回の認証基盤移行では既存ユーザーをログアウトさせることなく移行できることが求められました。

セキュリティ、可用性、サポートなどが総合的に優れていること

当時、要件を満たすために私たちが比較検討したIDaaS製品は以下でした。

セキュリティ、可用性、サポートなどを総合的に判断して、最終的にAuth0を選択しました。 また、パスワードを再設定させることなくユーザーを移行するための機能が充実しており、ユーザー移行の工数も下げられると考えました。

複数の方法を組み合わせた実際の移行

後に詳しく説明しますが、Auth0はパスワードハッシュも含めて移行が可能なユーザーデータのImport APIを提供しています。 このAPIを使って移行バッチなどを作成すればユーザーの移行を実現できます。

しかし、その間に新規ユーザーの登録があるとどうなるでしょうか。

バッチの組み方や既存の仕様などにもよりますが、移行中に新規登録したユーザーはタイミングによってはAuth0に未移行の状態になってしまう可能性があります。 サービス停止をすれば避けることもできますが、「ビズリーチ」の百万オーダの全ユーザーデータを移行するには2~3日程度かかることが想定され、ビジネス上不可能でした。

そこで私たちは、新規登録・ログインなどを行ったアクティブユーザーとその他のユーザーそれぞれで移行方法を分けて考えました。

大きく以下の2フェーズで移行を実現しました。

Ph.1 アクティブユーザーの個別移行

Auth0 Automatic Migration機能を利用したアクティブユーザーの個別移行

Auth0にはAutomatic Migration機能と呼ばれる移行機能があります。 Custom Database Action Script(以降、Action Script) と呼ばれるスクリプトファイルを書くことで自分たちのサービスのAPIやDBと連携し、ユーザーデータを移行することができます。

簡易化した構成、フローを以下の図に示します。

Automatic Migration機能を利用した個別移行の構成とフロー

移行はユーザーがメールアドレスとパスワードでログインしたときに行われます。 Auth0のユーザーDBにデータが存在しなければAction Scriptが動き、「ビズリーチ」からユーザー情報の取得を試みます。 このとき、「ビズリーチ」にユーザーデータが存在すればビズリーチユーザーIDを返し、メタデータとして付与した状態でAuth0のユーザー登録をすることで移行完了となります。

また、Auth0へのサインアップについてもAction Scriptを記述できます。 「ビズリーチ」にユーザーデータが存在している場合は登録済みとしてエラーを返すことで既存のビズリーチユーザーが未移行の状態でAuth0に登録されることを防いでいます。

次にソーシャル連携ユーザーの個別移行方法を説明していきます。

ソーシャル連携ユーザーの個別移行

Auth0はSocial Connection機能というかたちで様々なソーシャルサービスとの連携を提供しています。 しかし、Automatic Migration機能はあくまでメールアドレスとパスワードを用いたログインのときのみ利用でき、ソーシャル連携ユーザーの場合には利用できません。

ソーシャル連携ユーザーを移行する方法は様々なパターンがあると思いますが私たちは以下のようなフローで実現しました。

ソーシャル連携ユーザー移行の構成とフロー

移行はユーザーが任意のソーシャルサービスでログインしたときに行われます。 ソーシャルサービスにログイン完了すると、先にSocial Connection機能によってAuth0にユーザーが登録されます。 そして、認証が完了し「ビズリーチ」側に戻ってきた際にAuth0ユーザーにビズリーチユーザーIDを付与するという流れになっています。

重要なのは、認証フローにカスタマイズ処理を挟み込むRules(※1) を利用して、ID TokenにソーシャルサービスのユーザーIDを設定したことです。 これにより「ビズリーチ」側でID TokenからソーシャルサービスのユーザーIDを取得することができます。 そのソーシャルサービスユーザーIDを用いて既存のソーシャル連携ユーザーのビズリーチユーザーIDを特定します。 Auth0ユーザーへのビズリーチユーザーIDの登録は、Management API と呼ばれる管理API群の中のユーザー情報を更新するAPIで実現しました。

※1: 2023年3月時点ではRulesではなくActionsで拡張することが推奨されていますが、移行当時はActionsがGA版ではなかったのでRulesを利用しました

ここまでの仕組みで、アクティブユーザーをログアウトさせることなく自動的に移行することができました。 しかし、一部そのまま移行することができないユーザーもいました。

課題: RFCに準拠しないメールアドレスは移行できない

Auth0は登録できるメールアドレスの形式に制限がかけられています。 公式ドキュメントなどには記載がありませんが、Auth0のCommunityなどを参考にするとRFC5322に準拠しているようです。

「ビズリーチ」は認証基盤導入時点でRFC5322に準拠しないメールアドレスの登録・更新は許容していませんでした。 しかし、古いユーザーデータの中にはRFCに準拠しないメールアドレスで登録されてしまっているものがありました。 技術検証タイミングでRFCに準拠しないメールアドレスでAutomatic Migration機能を利用したところ、エラーが発生しAuth0に移行することができませんでした。

この問題は「RFCに準拠しないメールアドレスで登録している」かつ「直近アクティブ」なユーザーは少数であることを考慮し、例外的にメールアドレスを変更してもらうという意思決定を行いました。

可能な限りユーザーの離脱を防ぐため以下の点を考慮し、専用のメールアドレス変更フローに誘導しています。

Ph.2 Auth0 Management APIを利用したその他ユーザーの一括移行

ここまでに記載した仕組みでアクティブユーザーの移行は実現できたので、並行してその他の非アクティブなユーザーデータを移行できれば完了となります。

ユーザーデータのImport APIの利用

Auth0はManagement APIでユーザーデータのImport API を提供しており、パスワードも合わせて移行することができます。 custom_password_hash プロパティを利用することで様々なパスワードハッシュアルゴリズムのインポートに対応することができます。

私たちは移行用のバッチを組むことで実現しました。簡易化した構成、フローを以下の図に示します。

Import APIを利用した一括移行の構成とフロー

Import APIで一度にリクエストできるユーザーデータのサイズは500KBまでとなっているため、最大1000件単位となるようにパーティションして移行することにしました。 やっていること自体はシンプルで、移行対象データを取得し、Import APIが対応しているjson形式のファイルに変換、その後Import APIでリクエストするという流れになっています。

Import APIの実行中にAutomatic Migration機能による移行が行われる可能性もありますが、Import APIは登録済みデータは登録しない仕様になっているため問題ありませんでした。

また、一括移行を行う際は事前準備が重要です。

事前準備: 未移行データを特定する方法を用意しておく

一括移行を行うためには、自サービスで管理しているデータから未移行ユーザーを特定する必要があります。

私たちの場合、最終ログイン日時を利用して以下のような条件で未移行ユーザーを特定しました。

事前準備: リラン方法を用意しておく

大量データの移行は予期せぬ失敗がつきものであるため、バッチが異常終了した際にリランできる仕組みを用意しておく必要があります。 私たちはユーザーの識別子を入力することで続きからリランできるようにバッチロジックを組みました。

実際、Auth0側のインポート処理が稀に失敗することがあり、一回のバッチ実行で全ユーザーを移行しきることはできませんでした。 改めてリラン設計は大事であることを認識しました。

終わりに

「ビズリーチ」の認証基盤を刷新した背景とAuth0を活用したユーザーの移行方法について紹介しました。 Webサービスを構成する要素の一つである認証・認可機能は専門的な技術、知識を必要としますが、今ではIDaaSというかたちでマネージドなサービスを利用することができます。 既存のサービスの認証機能を移行することには困難もありますが、今回の記事の内容が少しでも参考になればと思います。

山本 凌平
山本 凌平

ビズリーチ事業部エンジニア。ビズリーチサービスのコアとなる基盤をいい感じにつくるお仕事をしている。2023年の目標は検索エンジニアと名乗れるようになること。