CADDiプロダクト横断の認証認可基盤を開発している話

注意! 2023年8月時点の内容となりますので、参考情報としてご覧ください。現在、アーキテクチャを見直し、同等の機能をより効率的に実現できる構成にして随時開発中です。機会が来たら新しいアーキテクチャの構成を紹介します

CADDi Platformグループの前多です。

私たちはCADDiのプロダクト横断の技術課題を解消するための活動をしています。 これまでの活動の詳細は 信頼性を高めるサービス基盤と技術選定を見てください。

これまでの活動はクラウドインフラや開発環境の整備などが大半でしたが、今後のCADDiのプロダクト開発を発展させるために、プロダクト共通で必要となるサービス基盤の開発にも着手しています。

現在私たちが開発しているのは、CADDiプロダクト全体で利用する想定の認証認可基盤です。 認証認可に関する製品は、Auth0などのSaaSをはじめ、他にもさまざまな製品があります。 私たちが開発している認証認可基盤は、これらの製品をただ導入するだけではなく、CADDiの事業形態に合わせて複数の製品や自作のサービスを組み合わせて構築したものです。

この記事では、CADDiのプロダクトの変遷および、独自の認証認可基盤を開発する理由と設計について記します。

なお余談ですが、私たちの認証認可基盤には Notcher というコードネームが付いています。 Notcher というのは切り込みを入れるためのハサミのことです。 かつての日本の鉄道では、紙の切符に駅員の方がハサミで切り込みを入れて、駅のホームに入ることができました。 つまり、切り込みを入れた切符はトークンであり、私たちの認証認可基盤も認証の結果としてトークンを発行することから、コードネームの由来となっています。

CADDiプロダクトの変遷

CADDiの事業は製造業の部品の発注から納品までを一気通貫で行う CADDi MANUFACTURINGから始まっています。 CADDi が企業から発注を受け、部品の製造をパートナーに依頼し検品との納品までを行うため、製造過程を管理するためのプロダクトを内製しました。 少々古い内容ですが、内製プロダクトについては こちらの記事などが参考になるでしょう。

その後も私たちは事業の内容や拡大に伴い、いくつかのプロダクトを作っていきました。 そこで、大量に発生する紙の図面と製品を効率よく管理するために、図面にIDを付与し、紙のデータをデジタル化して管理するプロダクトが生まれました。

そのプロダクトを開発した経験から、図面をデジタル化して有効活用するニーズは製造業に広くあるのではという仮説が生まれ、 AIによる類似図面の判定機能を追加してSaaS化したものが、CADDi DRAWERです。 ここに来て私たちは、内製プロダクトをSaaS化し、かつCADDi社内での利用から社外への展開をはじめました。

もちろん上記で触れた以外にもCADDiでは多くのプロダクトがあります。 そして、将来これらのプロダクト群を連携させ、社外にも展開していく構想があります。 そのためには次のような考慮が必要です。

  • プロダクト群を一貫したユーザー体験で提供する
  • APIを統一された方式で公開し、CADDiプロダクト群や サードパーティのツールとも連携する
  • 社内のプロダクト開発やプロダクト間連携の標準化・効率化・品質担保を推進する

この構想を実現するための下地として、認証認可基盤の開発を始めました。

認証認可基盤が必要な理由

CADDi のプロダクトは社内プロダクトから始まったため、社員が使えれば良いということで認証認可は最低限の実装となっていました。

CADDi では社員アカウントは Google Workspaceで管理しています。内製プロダクトの認証は Auth0のSocial Loginを使って、Google Workspaceのアカウントでログインしていました。 社内利用ということもあり、当時は社員のログインだけできればよく、認可の要件はありませんでした。

CADDi DRAWER の認証についても、当時は使い慣れたAuth0で利用者を管理することにしました。 CADDi Drawer専用のAuth0テナントでアカウント管理をしていて、利用者の所属企業などはユーザーの属性として保持しています。

この頃から、あるプロダクトから別プロダクトのデータを取得したいといった要望が出てきます。

しかし人による認証を念頭に置いていたため、API間の認証についての検討が十分ではありませんでした。 また、他にも社外提供を前提としたプロダクトの開発が始まったり、 CADDi Drawerでも会社ごとに独立した2FAやパスワードポリシーを設定したり、認可制御をしたいといった要望も出てきました。

拡大し続けるプロダクトについていけるように、今まで劣後してきた認証認可について考え直す必要性が出てきました。

認証認可基盤の設計

新しく認証認可基盤を設計するにあたり次のことを重視して設計しました。 また、OIDC/OAuth2 という認証認可の標準に則ることを前提としています。

  • マルチテナントのユーザ管理
  • 認証と認可の分離
  • セキュアなAPI間通信

マルチテナントのユーザー管理

これまでのプロダクトの認証は、プロダクトの性質に合わせて複数のAuth0テナントに分散していました。 また、CADDi Drawer ではユーザー属性に所属企業の情報を持たせることでユーザーの所属を設定していました。

この状態のままプロダクトを拡大していくと以下の点で運用しづらくなります。

まず、SSO(シングルサインオン)のような認証機能の使いやすさを大きく損ねます。 プロダクトごとにAuth0テナントのような認証サービスが分かれていると、複数のプロダクトにわたって共通でログインすることができません。 同じIDであってもプロダクトごとにログインをしたりパスワードが分かれてしまったりします。

従来では、認証サービスからGoogle workspace によるSocial Login によって社員は同じIDが使用できましたが、社外利用のユーザーにとってはそうでありません。 将来のプロダクト増加と利用企業増加に伴う、単一の認証サービスが必要です。

そして、単一の認証サービスであっても利用する企業ごとにユーザーを管理できるマルチテナントの機能も必要です。 現在のCADDi Drawerでは利用企業のユーザー管理はCADDiで実施しています。

将来的には、CADDiのプロダクトを利用する企業自身でユーザー管理を行うことを理想としています。 また、利用企業のセキュリティ基準に合わせたセキュリティポリシーや認証機能を実現することも想定しています。 現在でも企業ごとに、MFAやIP制限といった追加ルールを提供していますが、 さらに SAMLや SocialLogin、 パスワードポリシーの設定といった内容を企業ごとに独立して設定したいという要望に対応したいと考えています。

このような背景から、単一システムでマルチテナントに対応するユーザー認証サービスの実現を根幹として、認証認可基盤の設計を進めました。 当初は独自開発を考えていませんでしたが、OSS、有償製品、クラウドのサービスなど広く調査した結果、この要件を完全に満たしコスト的にも満足できるものはありませんでした。

ここでネックとなったのが、 「CADDiのプロダクトを利用するユーザーは、企業の中の一部門の方に限られる」 という背景です。 つまり、利用テナント数は多いがテナント内のユーザー数は決して多くないという想定です。 多くの製品では、テナント数の制約に上限があるか追加コストが必要でした。 また、ユーザー数が増えるほどコストが下がるような恩恵もこの形態では得られません。

そこで、認証サービスについては単一の製品で機能を実現することは諦め、低コストのID管理サービスに自分達で必要な機能を追加するという方針をとっています。

認証と認可の分離

マルチテナントのユーザー認証と同時に検討していたのが、認証と認可の分離です。 一般的にマルチテナントというと、認証認可に関する機能すべてが独立したものとして扱われます。 例えば、署名なOIDC/OAuth2のOSSである Keycloak のマルチテナント機能は、 テナントごとにユーザー管理だけでなく、OIDC/OAuth2クライアントの設定が全て独立するというものです。

CADDiのプロダクトにおけるマルチテナントの要件を検討した結果、すべての設定が独立していてはまずいケースがあることがわかりました。 CADDiにおけるテナントとはプロダクトを利用する企業(顧客やパートナー)です。 マルチテナントとして全ての設定が独立している場合、あるプロダクトはその企業専用のデータを持つことになります。 私たちのプロダクトも各テナントごとに専用の設定でデプロイする必要があります。 (もっと正確に言えば、プロダクトごとに発行したOAuth2クライアントを識別して取り回す必要があります。) また、プロダクトによっては企業間で問い合わや質疑応答をするといったような、単一のプロダクトを複数の企業で使うような形式もありえます。

つまり私たちのプロダクトは利用者がマルチテナントである必要はあるが、プロダクトは複数テナントのユーザーを扱える必要があります。

ここで思い至ったのは、ユーザーの認証とプロダクトの認可を分離するというアイデアです。 プロダクトのログインは単一だが、ログインのプロセス中で利用者が所属テナントを入力してログインします。 プロダクトは認証の結果として、テナントと利用者IDの2つの属性でユーザーを特定します。 似た仕組みとしては Auth0 Organizationsがありますが、 私たちの認証認可基盤では企業ごとの独立性を、より高めています。

このような認可・認証の分離を実現するために、Ory Hydraを認可サービスとして採用しました。 Hydra は OpenID Foundationによって認証された OIDC/OAuth2 のOSSのライブラリです。 特徴的なポイントは OAuth2の機能に特化していて、ログインに関する機能は Pluggableになっていることです。 詳細は Hydraのログインフローのドキュメントを参照してください。 Hydraはログインプロセスの中で設定に記載されたログインエンドポイントを呼び出し、ログインエンドポイントは認証結果をHydraに返します。 Hydraはその結果から、IdToken,AccessTokenを発行し、そのあとはHydraがログインセッションやトークンを管理します。

この仕様を守っていれば任意のログイン処理が利用できるため、前述のマルチテナント認証サービスをHydraと組み合わせることで要件を実現しています。

余談ですが、OryはKratosというID管理のOSSも提供していて、これらを組み合わせた OryというIDaaSを展開しています。 残念ながら、このサービスも私たちの想定するマルチテナントの機能はありませんでしたので、今回はHydraのOSS版を使用しています。

セキュアなAPI間通信

最後にAPI間通信です。

私たちがプロダクトに実装してきた認証処理は、主に人がログインする前提で設計していたため、プロダクト間で安全にAPIを呼ぶための仕組みやルールが不足していました。 過去にはブラウザでログインした後に、開発者ツールでアクセストークンを抜き出して curlでAuthorization Header にトークンを設定するといったようなことまでしていました。

そのほかに、内部利用を想定していたため、そもそも認証がかかっていないAPIもあります。

私たちが主に利用している Auth0 にも Mchine to Machine Token (M2M Token) という仕組みがあります。 ただし、M2M Tokenは月間の発行上限が決まっているため、内部利用ならともかく外部公開を前提とすると、想定外の使い方をするクライアントがいた場合、発行上限を迎える可能性が常にあるため、少々使い勝手が悪いものでした。 また、Auth0は複数のAPIの呼び出しを複数のAudienceとして設定できないという仕様 があるため、API連携を拡大していく方針と相性が良くありませんでした。

そこで、まずはAPI用のアクセストークンを発行する機能として、前述の Hydraを使用します。 Hydraは Client Credentials Grantによるアクセストークンの発行ができます。 また最近のアップデートによってWebHookでアクセストークンをカスタマイズできるようになったため、任意の属性をクライアントごとに付与できます。

次に、リソースサーバー(API)を管理する仕組みを自作しました。

リソースサーバーにはOAuth2スコープや、WebHookでトークンをカスタマイズするためのスクリプトを設定できるようにしました。 この仕組みとHydraを連携して、クライアントがアクセス可能なリソースサーバーとスコープを厳密に管理できるようにしています。

最後に、API側の実装を簡略化するプラクティスの提供です。

私たちの認証認可基盤はあくまでプロダクトの認証の結果としてトークンを発行し、それをカスタマイズすることだけです。 プロダクトで行うトークンの検証などはプロダクト側の責務ですが、なるべく実装を省力化することも視野に入れています。 私たちはプロダクトを GKEや Cloud Run上で稼働させていて、 GKE ではサービスメッシュを導入し、Cloud Runではサイドカーコンテナの設定ができるようになりました。 サイドカーコンテナ上で、アクセストークンの検証を実施したり、APIを呼び出した時にサイドカーコンテナで透過的にアクセストークンを発行してリクエストに付与するといった仕組みを検討・実装しています。

まとめ

これまでに解説した内容を図にまとめると次のようになります。

achitecture

複数テナントのユーザーアカウントを認証する認証サービスと、プロダクトからのOAuth2リクエストを処理する認可サービス(hydra)があり、 これらを設定するためのAdmin UIがあります。

認証認可基盤を利用するプロダクト(クライアントとリソースサーバー)は、Amdin UI上からクライアントとリソースサーバーの情報を設定しておきます。

認可サービスにOAuth2に従った認証リクエストを行うことで、その結果としてアクセストークンを得るので、アクセストークンをリソースサーバーへのリクエストに付与します。

その際、人によるログインであれば認証サービスのログイン処理が行われテナントを特定してログインを実施します。またその場合、IDトークンも同時に発行されます。

リソースサーバーではサイドカー自身でアクセストークンの検証を行い、問題なければ処理を続行します。

以上が現時点での認証認可基盤の内容です。 認証認可基盤はまだ完成しておらず、今後も次のような機能を実装予定です。

  • ユーザー管理APIとUIの作成
  • パスワードポリシーなどのセキュリティ機能
  • 運用監視の向上

また、認証認可基盤の開発がひと段落したら、他にもプロダクト横断の共通機能の開発を継続的に行っていく予定です。

私たちのグループでは以下のような方をお待ちしています。

  • 拡大していくプロダクトの開発を支えたり共通化することに興味がある方
  • 認証認可、OIDC/Oauth2に興味がある方
  • Platform Engineeringに興味がある方
  • SREに興味がある方
  • SREに興味がありつつ、開発もしたい方

興味がある方はぜひ一度お話しましょう。