OCXを支える技術 #4 Building the OCX Identity Provider System with Kratos and Hydra / Kratos と Hydra で作る認証認可基盤

Japanese follows English.

和訳は英語の次に記載されています。

Hello, I'm Atreya, a full-stack engineer at BBSakura Networks where I lead the UI team and oversee our identity provider's development and maintenance. I also collaborate closely with our API team, lending a hand in backend tasks if required, since I was involved in backend development as well during the early phases of the project.

Identity management has always been an integral component of modern applications and for software engineers seeking to construct a bespoke identity provider system, understanding the underlying architecture is essential. In this article, I'll be sharing insights into how I built our in-house identity provider system using open-source solutions: Kratos and Hydra.

What is an Identity Provider?

At the core of any robust digital solution is an identity provider (IdP) system. An IdP system is essentially the backbone that supports user authentication (verifying who the user is) and authorization (determining what the user can do).

The Core Components of our IdP System

Our IdP system comprises the following critical components:

  1. OCX Identity Provider: This server is at the heart of our system. Not only does it render the UI, but it also operates as a bridge between Hydra and Kratos. With Kratos already offering a configuration option to integrate Hydra, our Golang server focuses on rendering the UI, handling consent and logout requests, and forwarding the generated access token to our frontend console.

  2. Hydra: An open-source OIDC-compliant OAuth2 Server, Hydra's primary responsibility in our setup is to issue access tokens once the user is authenticated through Kratos. This ensures that our backend only needs to validate the access token for each API request.

  3. Kratos: A headless, open-source identity management system, Kratos provides the authentication mechanics. By being headless, it offers the flexibility to design a UI consistent with our system's overall design and functionality.

The following diagram illustrates how the three components talk to each other:

a sequence diagram illustrating how the three components talk each other. From left to right, the four participants are placed: OAuth 2 Client (Browser), OCX Identity Provider, ORY Hydra, and ORY Kratos. The sequence is as follows:  1. the OAuth 2 Client initiates an authorization code flow or an implicit flow request to Hydra. 2. Hydra verifies that there is no user session, i.e. the end user is not authenticated. 3. Hydra redirects to the OCX Identity Provider and sends a request with login challenge. 4. If there is no valid flow ID, the OCX Identity Provider retrieves the authentication details. 5. If there is no user session, the OCX Identity Provider follows link to '/self-service/login/browser?return_to="/login?refresh=true&login_challenge=xxx"'. 6. Kratos creates and stores a new login flow. 7. Kratos returns 'HTTP 302 Found <selfservice.flows.login.ui_url>?flow=<flow-id>' to the OCX Identity Provider. 8. OCX Identity Provider shows the login UI to the client (browser). 9. The user fills out the form and clicks login. 10. OCX Identity Provider submits the form to Kratos with 'POST /self-service/login/browser?flow=<flow-id>'. 11. Kratos validates the form and sets a cookie on the client (browser). 12. OCX Identity Provider redirects to Hydra's redirect URL with the verifier.  13. Hydra redirects to the consent endpoint '/oauth2/auth/requests/consent' with the concent challenge. 14. OCX Identity Provider obtains and accepts detailed information about the requested scope (OpenID, OCX Portal, etc.) 15. OCX Identity Provider redirects to the Hydra redirect URL with the verifier. 16. Hydra verifies grant. 17. Hydra sends an access token to the client (browser).

Why Kratos and Hydra?

Developing an identity provider from scratch is a daunting task. Instead of reinventing the wheel, we decided to stand on the shoulders of giants: Kratos and Hydra. Our choice was strategic. Both tools are open-source and built using Golang, a language we're familiar and comfortable with. This not only ensures a smooth integration with our backend service, but also makes it feasible for our team to contribute back, be it bug fixes or feature enhancements. I've personally fixed a bug (https://github.com/ory/kratos/pull/2507) and proposed a new feature (https://github.com/ory/kratos/issues/3037) to Kratos which I am currently developing.

On the frontend, we are using Next.js along with NextAuth.js. NextAuth.js simplifies the task of setting up custom OIDC-compliant OAuth2 servers with Next.js. All that needs to be done is register an OAuth client ID and client secret in Hydra and setting up the client ID and secret from Hydra into the NextAuth configuration. The result is a seamless bridge between our frontend console with our identity provider system.

It's worth noting that while Kratos is our choice for storing fundamental authentication details, the majority of business-centric data, including user roles, preferences, and more, is safely housed in our backend database. This clear demarcation ensures system clarity and efficiency.

Why not use services like Auth0 or Okta?

You might wonder, why not adopt mainstream services like Auth0 or Okta? Here's why:

  1. Control Over User Data: With our chosen setup, we have complete control over user data.

  2. Transparency: As Kratos is open-source, we're privy to how the data is saved. This transparency is paramount from a security perspective. Being open-source, Kratos affords us a detailed look into its workings. To illustrate, here's the schema of the tables that store user data.

an ER diagram illustrating the schema of the tables that store user data. There are seven tables (networks, identities, identity_credential_types, identity_credentials, identity_verifiable_addresses, identity_recovery_addresses, identity_credential_identifiers ), with columns and relationships for each table.

Final Thoughts

Incorporating existing, robust solutions like Kratos and Hydra allowed us to focus on optimizing our business logic rather than grappling with the foundational challenges of developing an identity provider from scratch. The synergy between these tools and our custom Golang server resulted in an IdP system that is not only efficient but also scalable and maintainable.

For those interested in diving deeper, Kratos and Hydra both boast excellent documentation ( https://www.ory.sh/docs/ecosystem/projects ) . While we chose the self-hosted open-source versions of Kratos and Hydra, it's worth noting that Ory, the company steering the development of these tools, offers a managed cloud service. This might appeal to those who prefer a managed service over the responsibility of hosting by themselves.

Additionally, an open-source example of Kratos-Hydra integration using Golang can be explored here ( https://github.com/atreya2011/go-kratos-test/tree/hydra-consent ). Building on top of proven platforms accelerates development and ensures that we're working with industry-tested security and performance standards. In essence, it's about smart engineering.

Happy coding!

Disclaimer: This article is not an endorsement of Kratos and Hydra.

日本語訳

こんにちは、BBSakura Networksのフルスタックエンジニア、アトレヤです。UIチームをリードしており、認証認可基盤の開発とメンテナンスも行っています。また、プロジェクトの初期段階ではバックエンド開発にも携わっていたため、必要に応じてAPIチームと密接に協力し、バックエンドのタスクにも手を貸しています。

「アイデンティティ管理」はモダンなアプリケーションに不可欠な要素であり、専用の認証認可基盤を構築しようとするソフトウェアエンジニアにとって、基盤となるアーキテクチャを理解することは極めて重要です。この記事では、オープンソースのソリューション、Kratos と Hydra を使用して認証認可基盤を構築した方法についての知見を共有します。

Identity Provider とは何か?

全ての堅牢なデジタル・ソリューションのコアには、認証認可基盤があります。認証認可基盤は基本的に、ユーザー認証(ユーザーが誰であるかを確認すること)と認可(ユーザーが何をできるかを決定すること)をサポートするバックボーンです。

OCXの認証認可基盤のコアコンポーネント

OCXの認証認可基盤は、以下の重要なコンポーネントで構成されています。

  1. OCX Identity Provider: このサーバーはOCXのシステムの中心にあります。UIをレンダリングするだけでなく、HydraとKratos間のブリッジとしても動作します。Kratosは元々Hydraを統合する設定オプションを提供しており、"OCX Identity Provider" はUIのレンダリング、コンセントとログアウト・リクエストの処理、生成されたアクセストークンをフロントエンドに転送することにフォーカスしています。
  2. Hydra: OIDC準拠のオープンソースOAuth2サーバであるHydraの主な役割は、ユーザがKratosを通して認証された後にアクセストークンを発行することです。これにより、OCXのバックエンドはAPIリクエスト毎にアクセストークンを検証するだけでよくなります。
  3. Kratos: オープンソースのヘッドレスなアイデンティティ管理システムであるKratosは認証の仕組みを提供します。ヘッドレスであることで、OCXのシステム全体のデザインと機能性に合致したUIを設計する柔軟性を提供します。

実際の処理の流れは、以下のシーケンス図を参考にしてください。

コアコンポーネント間のやりとりを示すシーケンス図。左から、OAuth 2 Client (Browser), OCX Identity Provider, ORY Hydra, ORY Kratos の 4 つの登場人物が配置されています。シーケンスは以下の通りです。1. OAuth 2 Client は Hydra に対して 認可コードフローまたはインプリシットフローのリクエストを開始します。2. Hydra はユーザーセッションがない、つまりエンドユーザーが認証されていないことを確認します。3. Hydra は OCX Identity Provider にリダイレクトし、認証のためのチャレンジを含むリクエストを送信します。4. 有効なフロー ID がない場合、OCX Identity Provider は認証情報の詳細を取得します。5. ユーザセッションがない場合、OCX Identity Provider は Kratos に対して '/self-service/login/browser?return_to="/login?refresh=true&login_challenge=xxx"' のリンクを辿ります。6. Kratos は新しいログインフローを作成および保存します。7. Kratos から OCX Identity Provider に 'HTTP 302 Found <selfservice.flows.login.ui_url>?flow=<flow-id>' を返します。8. OCX Identity Provider はクライアント(ブラウザ)にログイン UI を表示させます。9. ユーザーがフォームを埋めてログインをクリックします。10. OCX Identity Provider は Kratos に対して 'POST /self-service/login/browser?flow=<flow-id>' でフォーム送信します。11. Kratos はフォームの有効性を検証し、クライアント(ブラウザ)に cookie を設定します。12. OCX Identity Provider はベリファイアとともに Hydra のリダイレクト URL にリダイレクトします。13. Hydra はチャレンジとともに同意エンドポイント '/oauth2/auth/requests/consent' にリダイレクトします。14. OCX Identity Provider は要求されているスコープ(OpenID, OCX ポータルなど)について詳細な情報を取得し、受け入れます。15. OCX Identity Provider はベリファイアとともに Hydra のリダイレクト URL にリダイレクトします。16. Hydra は権限を確認します。17. Hydra はクライアント(ブラウザ)に対してアクセストークンを送信します。

なぜKratosとHydraなのか?

認証認可基盤をゼロから開発するのは大変な作業です。車輪を再発明するのではなく、我々開発陣は巨人(HydraとKratos)の肩の上に立つことに決めました。この選択は戦略的でした。KratosもHydraもオープンソースで、開発陣が慣れ親しんでいるGolang言語を使って開発されています。これは、OCXのバックエンドとのスムーズな統合を保証するだけでなく、バグ修正や機能拡張などにOCXのチームメンバーが貢献することを可能にします。私自身、バグ ( https://github.com/ory/kratos/pull/2507 ) を修正し、現在開発中の新機能 ( https://github.com/ory/kratos/issues/3037 ) をKratosをメンテしているOry社に提案しました。

フロントエンドでは、Next.jsとNextAuth.jsを使用しています。NextAuth.jsでNext.jsにカスタムのOIDC準拠のOAuth2サーバをセットアップする作業が簡単になります。HydraにOAuthのクライアントIDとシークレットを登録し、NextAuth.jsのコンフイグにこのクライアントIDとシークレットを設定するだけです。その結果、OCXフロントエンドのコンソールと認証認可基盤をシームレスにつなぐことが可能になります。

Kratosは基本的な認証情報を保存するために使用されており、ユーザのロールなどを含むビジネス中心のデータの大部分は、違うバックエンドデータベースに安全に格納されています。この明確な区分により、システムの明瞭性と効率性が保証されます。

なぜAuth0やOktaのようなサービスを使わないのか?

Auth0やOktaのような主流のサービスを採用していない理由は以下の通りとなります。

  1. ユーザデータの管理: ユーザデータを独自に管理することができます。

  2. 透明性: Kratosはオープンソースであるため、データがどのように保存されるかを知ることができます。この透明性はセキュリティの観点から最も重要なことです。オープンソースであるため、その仕組みを見ることもできます。例として、ユーザデータを格納するテーブルのスキーマは以下の通りとなります。

ユーザデータを格納するテーブルのスキーマを表す ER 図。7 つのテーブル(networks, identities, identity_credential_types, identity_credentials, identity_verifiable_addresses, identity_recovery_addresses, identity_credential_identifiers )があり、各テーブルのカラムや関係性が書かれています。

最後に

KratosやHydraのような既存の堅牢なソリューションを組み込むことで、認証認可基盤をゼロから開発する基礎的な課題に取り組むよりも、ビジネスロジックの最適化に集中することができました。これらのツールとカスタムGolangサーバであるOCX Identity Providerとの相乗効果により、効率的であるだけでなく、スケーラブルでメンテナブルな認証認可基盤を構築することができました。

KratosとHydraはどちらも優れたドキュメントがあるのでぜひ読んでみてください ( https://www.ory.sh/docs/ecosystem/projects ) 。OCXではKratosとHydraのセルフホスト型のオープンソース版を選びましたが、これらのツールの開発を主導しているOry社がマネージドクラウドサービスも提供しています。これは、自分でホスティングして責任を負うよりも、マネージドサービスが良いというチームには合っていると思います。

さらに、Golangを使用したKratosとHydraのブリッジ実装のオープンソース例をこのレポ ( https://github.com/atreya2011/go-kratos-test/tree/hydra-consent ) で見ることができます。実績のあるプラットフォームの上に認証認可基盤を構築することで、開発を加速し、業界でテストされたセキュリティとパフォーマンスの標準を確実に使用することができます。要するに、スマートエンジニアリングということです。

ハッピー・コーディング!

www.DeepL.com/Translator (無料版)で翻訳し、修正したものです。