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:
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.
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.
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:
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:
Control Over User Data: With our chosen setup, we have complete control over user data.
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.
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.
それと普段 cilium/ebpf を利用して go で書いて開発してるのですが、その pure go な実装のローダーの上で BPF_PROG_TYPE_STRUCT_OPS が使えず普段使ってるもので動かず悲しい気持ちになりました。maptype はあるんですけどね...誰もやらなかったら試しに改造してパッチを出そうかなと思います。
set interfaces ethernet eth2 address '2001:db8:817:1::fffe/64'
set interfaces ethernet eth3 address '2001:db8:817:2::fffe/64'
set service dhcpv6-relay listen-interface eth2
set service dhcpv6-relay listen-interface eth3
set service dhcpv6-relay upstream-interface eth1 address '2001:db8:817::3'
set service router-advert interface eth2 prefix 2001:db8:817:1::/64
set service router-advert interface eth3 prefix 2001:db8:817:2::/64
DHCPv6 サーバ : isc-dhcp-server
PD 回線で DHCPv6-PD にてプレフィックスを委譲し,また PD 回線と RA 回線の両方で DNS キャッシュサーバのアドレスなどを配布する,DHCPv6 サーバです。
DHCPv6 サーバには,WIDE Project による実装や,ISC による実装 isc-dhcp,同じく ISC による Kea DHCP Server などがあります。今回は一般によく使われておりシンプルな isc-dhcp を使用しました。
DHCPv6 はユーザ収容を行う VyOS でもサポートしていますが,細かい制御を行うために VyOS は DHCPv6 Relay とし DHCPv6 Server は別途立ち上げました。
# configure
# set system services ssh
# set system login user akiyama authentication plain-text-password
# set system login user akiyama class super-user
# commit and-quit