既存RailsアプリをSSO化して本番環境で活用した話(前編)

この記事は 2021/09/16 (木) に行われた WESEEK Tech Conference の内容をまとめたものです。
発表前半部分の 「OpenID Connect に対応した認証基盤を構築した話」 を掲載しております。

既存RailsアプリをSSO化して、本番環境で活用した話【WESEEK Tech Conf #12】

目次

インターネットマルチフィード様紹介

インターネットマルチフィード(株)とは?

MF社では、JPNAP と transix というサービスを提供しております。

JPNAP は IX(Internet eXchange) のサービスで、様々な事業者様にご利用いただいております。
transix は NTT 東・西日本が提供しているフレッツ光の IPv6 IPoE 接続を事業者様向けに提供しているサービスです。

各サービスの詳細な内容については、こちらをご覧ください。

https://www.mfeed.ad.jp/

WESEEK との関係 (1/2)

前提として、WESEEK はインフラからアプリまでワンストップでリソースを提供することができます。

そこから、インターネットマルチフィード様から様々な問題の解決、改善を目的に、弊社代表の武井に相談がありました。

2015 年から共同プロジェクトを開始して、6年以上経っています。

WESEEK との関係 (2/2)

開発フローに関する詳しい説明については、企業を超えたアジャイル+Railsを利用した開発の成功事例【WESEEK Tech Conf #8】 をご参照ください

実現したこと

実現したこととしては、シンプルですが、「社内システム(Rails 製)のアカウント情報を利用して、ユーザ向けの複数サイトにて、共通 ID でログイン可能に」したことです。

ドキュメントサイト・ポータルサイト・Looking Glass と図に複数サイトを記載していますが、全て社内システムにあるアカウント情報にてログインができるようになっています。

SSO(Single Sign On) とは

モチベーションや実装の詳細に入る前に、この発表における「SSO(Single Sign On)とは何を指すか?」という定義をしておきます。

Qiita Team 様より引用しているのですが、「1 つの ID とパスワードで複数のサービスにログインする仕組み」をこの発表では SSO と呼んでいます。

本来であれば、画面右側のように各サイトごとに ID とパスワードの組が必要になりますが、SSO を実現することで、画面右側のように 1 つの ID とパスワードの組で各サイトにログインができるようになります。

モチベーション

それでは今回実現するに至ったモチベーションのご紹介から始めていきます。

モチベーション (1/3)

まず、MF 様の社内には nautilus と呼ばれる業務の中枢を担う社内システムがありました。

これは MF 様と WESEEK との共同で作った内製システムであり、顧客情報の他に機器管理なども担うシステムとして稼働しています。

また、他の要求として、「nautilus で取り扱っている情報を顧客の方にも閲覧できるように公開したい」という要求もありました。

そこで、ポータルサイト(My.JPNAP) の発足に至りました。

My.JPNAP スクリーンショット

こちらが ポータルサイト(My.JPNAP) のスクリーンショットです。

1行1行がお客様の回線契約を表しています。回線の契約 ID や契約中の速度などが確認できるようになっています。

モチベーション (2/3)

ポータルサイトの公開後、他にも様々な要求があがりました。

  • ポータル意外に顧客向けのドキュメントを公開する場所とか欲しいよね
  • ポータルサイト意外にも認証・認可の機構を入れたいよね

これらの要求をもとに懸念点の洗い出しを行いました。ざっくばらんに洗い出しを行ったところ下記のような懸念点が浮上しました。

  1. サイトごとに認証のロジックを実装するの?
  2. ユーザが増えたら都度都度、各サイトにアカウント追加するの?
  3. 認可はどうする? (ユーザ A はサイト A は見えて、サイト B は見えない)
  4. サイトが今より増える場合は?
  5. 社内システムは絶賛稼働中で停止しづらい
  6. 新しい仕組みを導入するにしても、社内システムにアカウントがすでにあるし ... (移行コスト)

これらを整理して、どのように対応していくかの方針を次のスライドに記載しています。

モチベーション (3/3)

1, 2, 3, 4 の部分は認証と認可の一元管理を行うことで解決可能だと考え、SSO + α(アルファ) で対応することにしました。

また、5 の部分に関しては「なるべく社内システムを現行から落とさない(ダウンさせない)ようにする」ことを念頭に置きました。

加えて、6 の部分に関しては移行コストとのバランスを考え、既存アカウントを活用する方向に舵を切りました。

技術検討

ある程度考えをまとめることができたため、ここから具体的な技術の検討に入っていきます。

下記のような順で技術検討を行いました。

  • 前提条件の検討
  • プロトコルの検討
  • 実装の検討

前提条件の確認

まずは実現するにあたって、前提条件の確認を行いました。大きく分けて 3 つの項目としています

  1. 社内システム(nautilus) にはすでに社員などのアカウント情報がすでにある状態である
  2. セキュリティに関する部分は重要であるため、汎用的な技術を使うこと
  3. 社内システムのアカウント情報を別システムに移すのは移行コストなどを考えると大変である、ということ

これらの前提条件を元にプロトコルの検討から入りました。

プロトコルの検討

SSO を実現するにあたって他にもプロトコルはありますが、主に 2 つの代表的なプロトコルが挙げられます。

SAML(Security Assertion Markup Language) と OpenID Connect を候補として挙げました。

SAML のメリットとしては

  • 枯れている
  • 柔軟な認可管理が可能

という点が特徴であり、SAML の定義次第で、細かく認可の制御が可能になっています。
また、歴史もあり、「枯れている」というのは大きいポイントです。

一方、デメリットとしては

  • ネットの情報源が少ない
  • 認可における制御が複雑 (そうに見えた)

という点です。

1つ目の「ネットの情報源が少ない」という点は SAML に関する詳細な解説などは現在ではパッと見つかりづらかったという印象があります。(もちろん、あるべき箇所にはあると思いますが ...)

また、2 つ目の「認可における制御が複雑」ということについてはメリットの裏返しになっているかな、と思いました。
様々な認可における定義が可能ですが、様々なことが定義できるため、管理面では複雑である印象を受けました。

OpenID Connect のメリットとしては

  • ネットの情報源が(比較的)豊富

であるという点です。OpenID Connect 自体が新しいプロトコルである、という点もあると思いますが、検索した時に解説されているサイトが多い、という印象があります。

デメリットとしては

  • 認可に関する情報が少ない
  • SAML ほどの柔軟性は (おそらく) ない

という点です。OpenID Connect のドキュメントに関しても、認可に関する記載が SAML と比べて比較的少ない点が気になりました。また、拡張性という点においても、SAML ほど拡張性がないのではないか、というポイントが気になりました。

両方ともメリット・デメリットがありますが、情報の検索がしやすいことと、そこまで柔軟な認可管理をしない、といった理由を元に OpenID Connect を採用することにしました。

OpenID Connect とは?

「OpenID Connect 1.0 is a simple identity layer on toop of the OAuth 2.0 protocol」と英文を記載していますが、端的に述べれば「OAuth 2.0 プロトコルに identity の概念を足したもの」です。

OAuth 2.0 はなにか、という点ですが、こちらは身近な例があります。

最近では各サービスに「Google でログイン」「Twitter でログイン」ボタンなどを見かけると思いますが、そちらが OAuth 2.0 を使っているものになります (厳密に言えば、 OAuth 1.0 を使っているサービスもあります)

「OAuth 認証」について

ここでは便宜上「OAuth 認証」と呼んでいますが、OAuth では具体的に「ユーザを識別する規定」はありません。

一方で「認証っぽいことができてるじゃない?」と思われますが、これはあくまで各社が独自でユーザを識別する方法を実装しているためです。「プロフィール API」というものを用いて個人の識別を行っています。

次に OAuth と OIDC のフローの違いを示します。

OAuth と OIDC の比較

図の詳細なフローに関してはここでは述べませんが、赤字で書かれているところに注目してください。

OAuth 2.0 では「1.アクセストークン取得」「2.ユーザ情報取得」「3.取得したユーザー識別子で認証」という順番になっています。

一方 OIDC (OpenID Connect) では「1. ID トークン取得」「3. ID トークン内のユーザー識別子で認証」となっています。

OAuth 2.0 ではトークンの取得をした後にユーザの識別を行っていますが、OIDC では最初の段階でユーザの識別ができるように規定されています。

これが「OAuth 2.0 に対して identity の概念を足した」ということになります。

実装の検討

プロトコルに関する調査も進み、採用するプロトコルも決定したため、具体的な実装の検討に入ってきます。

大きく分けて 2 パターンを考えました。

  • 自前実装
  • OSS を利用する

1つ目の「自前実装」は論外ではありますが ... 一方 OpenID Connect の実装は比較容易です。ですが、「正しく」実装するポイントに力をかけるのは専門家におまかせした方がスケールするだろう、という思いから、自前実装は却下しました。

2つ目の「OSS を利用する」に関しては、調査してみたところ、2 つのプロダクトが候補に挙がりました。

Keycloak は高機能であり、Keycloak 自身でもアカウント情報を保持できますが、LDAP などに認証を Proxy してくれる機能もあります。弊サービスである GROWI.cloud でも Keycloak の技術検証を行っていますが、今回の要件上「多機能すぎる」というポイントが気になり、却下となりました。

Hydra(ハイドラ) は「既存の認証リソースを活用して、OpenID Connect 対応にさせる OSS」です。

こちらの Hydra の方が今回の用途にマッチしていたため、採用することにしました。

Hydra の紹介

Hydra (ハイドラ) は ORY 社が Go 言語で開発している OSS です。

OAuth 2.0 and OIDC Certified Server とある通り、OpenID Connect を話してくれるサーバーです。

やってくれることとしては「Hydra と既存ソフト(今回は社内システム)が協調することで OpenID Connect プロトコルを代理で話してくれる」というものです。既存ソフトと Hydra とのやり取りは HTTP を使って行っています。

一方 Hydra がやってくれないことは「ユーザアカウントの管理」です。具体的には、ユーザ登録の管理や、パスワードリセットなどのアカウントデータの管理を行わない、ということになります。

それらは既存ソフト側が対応する必要があります。社内システム(nautilus) では元々そういった機能があるため、Hydra と連携するにあたって、不足はありませんでした。

結果的にどういった構成になったかは、システム構成で紹介させていただきます。

システム構成 (導入前)

導入前の状態は非常にシンプルなシステムです。いわゆる Web サービスのフロントエンドとバックエンドの構成になっています。My.JPNAP は SPA で作られているのですが、ブラウザから、nautilus にある API を叩いてログインを行っています。

図の中にある点線部分は「SSO に対応させたいサイト」を表しています。

システム構成 (導入後)

導入後の図は複雑化しているように見えますが、赤字と赤線の部分のみ見ていただければ問題ありません。

nautilus の前段に Hydra が挟まる構成になっており、My.JPNAP が Hydra にリクエストを送り、Hydra と nautilus が相互に情報をやり取りすることで認証を実現するようになっています。

Hydra が前段に挟まる構成になったことにより、OpenID Connect による認証を実現できるようになりました。

ログインの流れをもう少し詳しく

こちらは「Login Flow | ORY Hydra」からの引用です。画面左側がポータルサイト、nautilus が画面右側に属しています。

細かいフローは(記事内では)図を見ていただくとして、このようにポータルサイト・Hydra・nautilus が協調してログインを実現していることが分かるかと思います。

[再掲] モチベーション(3/3)

ここまでで先述したモチベーションを振り返ってみましょう。

Hydra を利用したことで、「一元管理」「なるべく社内システムを現行から落とさないように」「既存アカウントを活用する」が実現できました。

Rails アプリで追加実装した箇所

では、具体的に社内システムである Rails アプリに追加で実装した箇所の紹介をしていきます。

1つ目のログイン用の画面は、スライドの画面右側にある通りのログイン画面を実装しました。

2つ目は Hydra とログイン時の情報をやり取りするための API エンドポイントを各種実装しました。個数としてはあまりなく、いくつかの Endpoint を実装するだけで対応できました。

3つ目の社内システムで受け取った Token が正しいかどうかを検証する箇所は、API アクセスがあるたびに Token が社内システムに送信されてくるようになりましたが、その Token が正しいかどうかを社内システムから Hydra に問い合わせる、という機構を実装しました。

苦労したポイント

苦労したポイントはスライドに書いてあるとおりですが、主に Cookie やセッションのハンドリングが苦労したポイントです。

Rails では認証を担う devise という gem が有名だと思いますが、セッション管理などは Hydra に任せる必要があり、devise が意図せず Cookie を送ってしまうと、デバッグが難解になることがありました。

一方、多要素(2FA)認証はあえて Cookie を使って適切にハンドリングする必要がありました。Hydra は 2FA に関する機能は持っていないので、2 FA に対応する場合には社内システム側に機能を追加する必要があります。

1 つ目の話と相反する点がありますが、「ここでは Cookie は送ってよいが、こちらではしない」というハンドリングが主に苦労したポイントとなります。

SSO 対応後の活用例

SSO 対応をした後の活用例を紹介します。

oauth2-proxy/oauth2-proxy を利用して、スタティックなサイト(JS や HTML のみで構成されているもの)も SSO 対応にすることができました。

また、OpenID Connect 対応になったため、インフラ側の技術検討が促進されました。こちらは「後編」にてご紹介いたします。

[再掲] システム構成 (導入後)

こちらは oauth2-proxy/oauth2-proxy を利用した例を青い線で囲っています。Docs.JPNAP、LookingGlass に関しても前段に oauth2-proxy を挟むことで OpenID Connect を利用した SSO を実現することができるようになりました。

まとめ

前半部分のまとめです。

1つ目は「既存の業務システムの認証データと Hydra を活用することによって OpenID Connect に対応することができ、SSO を実現できた」ことです。Hydra を利用することによって、様々な前提条件を満たす構成を実現することができました。

2つ目の「OpenID Connect 対応になったことによって、インフラ側でも OpenID Connect を前提とした技術検討ができるようになった」は後半にてご紹介いたします。

著者プロフィール

大谷津 昂季

株式会社WESEEK / システムエンジニア

2014 年に WESEEK に入社。
WESEEK ではサーバサイド(主に Ruby on Rails)とインフラエンジニアを担当しています。

株式会社WESEEKについて

株式会社WESEEKは、システム開発のプロフェッショナル集団です。

【現在の主な事業】

  1. 通信大手企業の業務フロー自動化プロジェクト
  2. ソーシャルゲームの受託開発
  3. 自社発オープンソースプロダクト「GROWI」「GROWI.cloud」の開発

GROWI

GROWIは、Markdown記法でページを記述できるオープンソースのWikiシステムです。

GROWI.cloud

GROWI.cloudはOSSのGROWIを専門的知識がなくても簡単に運用・管理できる、法人・個人向けの商用サービスです。

大手SIer・ISPや中小企業、大学の研究室など様々な場所でご利用いただいております。

【主な特徴】

  • テキストも図表もどんどん書ける、強力な編集機能
  • チーム拡大に迅速に対応できる管理者向け機能を提供
  • 充実した機能・サポートでエンタープライズにも対応

【導入事例記事】
インターネットマルチフィード株式会社様

株式会社HIKKY(VR法人HIKKY)様

WESEEK Tech Conference

WESEEK Tech Conferenceは、株式会社WESEEKが主催するエンジニア向けの勉強会。WESEEKに所属するエンジニアが様々なテーマで発表を行う予定です。

次回のWESEEK Tech Conferenceのテーマは「Rails+RSpecで気軽に始めるテスト」!
9/30(木)の19:00~20:00開催予定です。

ついつい書くのが億劫になってしまうテストコード。気軽にテストを書いてプロダクトの品質を高める、WESEEK流の実践方法を紹介します。
また、開発と書いたテストコードの実行をどのように行っているのか、CIについても簡単にお話します。

現在、connpassやTECH PLAYで参加受付中です。皆様のご参加をお待ちしております!
https://weseek.connpass.com/event/224673/
https://techplay.jp/event/830907

一緒に働く仲間を募集しています

東京の高田馬場オフィス、大分にある別府サテライトオフィスにてエンジニアを募集しております。

中途採用だけではなく、インターンシップも積極的に受け入れています!

詳しい募集要項は、弊社HPの採用ページからご確認ください。