RailsでOpenID Connectするときの前提知識

# 初めに
既存のRailsでシングルサインオン(以下SSO)を実装したいときによく出てくるのが、OpenID Connect。入門解説やフロー解説はありますが、実際にRailsで実装するときはどうすれば良いのかわからない...ということで調べてまとめてみました。実装前の前提知識ということで、ざっくり説明します。

## 参考

- [第一回 認証基盤のこれからを支えるOpenID Connect | オブジェクトの広場](https://www.ogis-ri.co.jp/otc/hiroba/technical/openid-connect/chap1.html)

# OpenID Connectとは?
分かりやすい解説がすでに幾つかあるので、そちらに任せます。ここの例はすでに存在するサードパーティ(Yahoo)のアカウントを使って自社サービス(Gree)のアカウントを紐づけるSSOの概要です。
<iframe src="//www.slideshare.net/slideshow/embed_code/key/DAgcEe0YyhsunB" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/kura_lab/openid-connect-id" title="OpenID Connect 入門 〜コンシューマーにおけるID連携のトレンド〜" target="_blank">OpenID Connect 入門 〜コンシューマーにおけるID連携のトレンド〜</a> </strong> from <strong><a href="https://www.slideshare.net/kura_lab" target="_blank">Masaru Kurahayashi</a></strong> </div>

スライド67ページ目の「サービス導入例」が分かりやすいです。この辺をみてある程度流れを確認した後に、初めのほうの説明見るとイイですね。ただ、構成要素について詳しい解説がないので、ここで補足します。

## 構成要素
- OpenID Provider(OP)/Identity Provider(IdP)
    - ユーザの認証を行うサーバ。
    - WebAPIとログイン/ログアウト用のWebUIを備えています。
- Relying Party(RP)
    - OPにユーザ認証を依頼するサーバ。
    - OSSを使ってログインしたいアプリケーションサーバです。

RailsでSSOやるときは、RPがRailsだと思ってくれればほぼ問題ないです。ただし、RailsでOP作れるのでこれと混同しないように注意してください。下記のdoorkeeperはOP用のgemです。

- [doorkeeper: Doorkeeper is an OAuth 2 provider for Ruby on Rails](https://github.com/doorkeeper-gem/doorkeeper)

OAuth2 Providerとなってますが、OpenID Connectは、OAuth2をベースに拡張させたものなので、OAuth2のコンポーネントが流用できます。Google/TwitterとSSOやる場合はすでにOPが用意されているのでそれを使います。自身の複数サービスをすべて同一アカウントで管理するタイプのSSOは、自前のOPを用意する必要があります。

# フローについて
OpenID Connectについて調べると大体ログインのフローばっかり出てきますが、**もちろんログアウトのフロー**もあります。それもざっくり解説します。
## ログイン
- Authorization Code Flow
    - OPとRPが直接やり取りするフロー。
    - MVCとかのアプリケーション向け。基本これ使えばOK。
- Implicit Flow
    - OPとRPが直接やり取りできない環境でも使えるフロー。
    - SPAとかのアプリケーション向け。ただし現在は**非推奨**。
        - [OAuth 2.0 の認可レスポンスとリダイレクトに関する説明 - Qiita](https://qiita.com/TakahikoKawasaki/items/8567c80528da43c7e844#%E3%82%A4%E3%83%B3%E3%83%97%E3%83%AA%E3%82%B7%E3%83%83%E3%83%88%E3%83%95%E3%83%AD%E3%83%BC%E9%9D%9E%E6%8E%A8%E5%A5%A8)
- Hybrid Flow
    - 単一クライアントに対して、2つのアクセストークンを発行する方式。
    - ネイティブアプリやSPAとかのアプリケーション向け。
        - [ハイブリッドフローによる 2 つのアクセストークンの発行 — Authlete ナレッジベース](https://kb.authlete.com/ja/s/oauth-and-openid-connect/a/hybrid-flow-access-tokens)

## セッション管理 & ログアウト
- Session Management 
    - Webページにiframe埋めこんで、RP側のログイン状態をOPに通知して不整合が起こらないようにする。
    - RP側がログアウトしていれば、OP側のログアウト画面にリダイレクトさせる。
        - [OpenID Connect Session Management 1.0 でフロントチャネルからOPのログイン状況をRPに伝える](https://dev.classmethod.jp/articles/openid-connect-session-management/)
- Front Channel Logout
    - OPとユーザ(のブラウザ)が直接通信するログアウト方式を指します。RPが複数あるイメージです。
    - RP側にログアウト用URLを事前に定義し、OP側にそのURLを登録しておく。
        - RP1でログアウト(RP1のログインセッション削除)
        - OPにリダイレクト(OPのログインセッション削除)
        - OPでログアウトページを表示(iframe埋め込み)
        - iframe経由で他RPのログアウトURLが叩かれる(他RPのログインセッション削除)
    - というようなイメージ
- Back Channel Logout
    - サーバーサイド同士の通信。OPとRPで通信するログアウト方式を指します。
    - Front Channel Logoutのiframe経由でやっていた通信をすべてサーバサイド同士で通信するイメージ


## 参考

- [現在仕様策定中の OpenID Connect RP-Initiated Logout 1.0 とは - r-weblife](https://ritou.hatenablog.com/entry/20151104/1446597256)
- [Youtube | Single Logout Dilemma #IAM4Developers Meetup (Mountain View/Toronto/London)](https://www.youtube.com/watch?v=ObWSeNYkh8s)
- [OpenID Connect Single Logout | Curity](https://curity.io/resources/learn/openid-connect-logout/)

# Railsで実装どうすればいいの?
RP側でOP側に対するHTTPリクエスト/OPからのHTTPレスポンスを適切に処理する部分を実装する必要があります。フルスクラッチで作っている方もいますが、基本的に[omniauth](https://github.com/omniauth/omniauth)を使ったほうが楽です。

## サードパーティのアカウントと自身のサービスと紐づけるSSO
特にサードパーティ側がomniauthを自身の認証用にカスタマイズしたgem(omniauthのストラテジー)を配布してくれていることが多いので、それに従って実装すればOKです。

このパターンのSSOで特に考慮するポイントは、サードパーティと自分のサービスでそれぞれ独立したアカウントがあり、サードパーティのOPが公開さてている点です。公開されている分様々な攻撃にさらされる恐れがあるほか、攻撃者のアカウントに被攻撃者のアカウントを紐づける攻撃など攻撃のバリエーションが多いです。

基本的にサードパーティのアカウントでログインできれば良いので、ログアウトは同期しない実装にできます。そもそもログアウト用のAPIが公開されてないパターンもあります。

## 自身が持つ複数のサービス間で実施するSSO
まず自前でOPを用意する必要があります。OPは自前で構築するか、マネージドサービスのOP使う方法など様々です。また利用するOPによりますが、自分自身でomniauthのストラテジーを作る必要があるため若干面倒です。
その場合は、[omniauth-oauth2](https://github.com/omniauth/omniauth-oauth2)をベースに自前のストラテジーを作成するのが一番手っ取り早い方法です。

ログイン画面を表示する関係でOPが非公開というのは無理ですが、APIにアクセスする経路をだいぶ限定できる分、攻撃手法は限定されます。

このパターンはログインだけではなく、ログアウトも実装する必要があります。

# 最後に
OpenID Connectを学習する際に、全体像がつかみづらかったためこの記事を作成しました。この記事を読んだうえでほかの細かい記事読むとより理解が深まると思います。
あと、時間があるときにomniauth-oauth2のストラテジーを作成した時の内容も記事にしたいと思います。