こんにちは、サーバーサイドエンジニアの金澤です。
みなさんSSOしてますか?
今日は弊社の管理ツールなどで導入しているSSOについてお話しします。
現状
大きく分けて二通りの方法で実現しています。
- awsのalbでopenid connect(以下oidc)
- google G suiteのsaml
よくあることですがなぜ二つあるのかについては歴史的経緯以外の理由はありません。
利用する側から見た使い勝手もほぼ変わらないので、実装面などでの差をお話ししたいと思います。
導入方法
それぞれの導入方法について軽くご紹介します。
両者に共通することですが、ヘッダに認証情報が入っているのでhttpsのみの運用が強く推奨されています。
samlの設定例
googleのドキュメントはこちらにあります。
railsアプリケーションなので、OmniAuthとそのsaml strategyを使いました。
admin consoleからappを追加する
google G suiteのadmin権限が必要です。
admin画面から登録しましょう。
このアイコンが目印です。

SSO URLとEntity IdというのがOmniAuthの設定に出てくるidp_sso_target_urlとissuerなのでメモしておきましょう- 証明書もダウンロードしましょう
- ユーザーの情報が欲しいときは、attribute mappingに追加
- 弊社はメアドと名前が欲しかったので以下のようにしました。

また、googleからcallbackを呼ぶ必要があるのでそのurlを登録する必要があります。 デプロイする環境が決まったら設定しましょう。

entity idはユニークであればなんでもいいのですがurlを入れておくのが分かりやすいし必ずユニークになるので便利かと思います。
有効化する
作った時点ではAppの設定がOff for everyoneなのでonに設定する必要があります。
特定の人にだけ公開したい場合は、下記画面から手で設定することができます。

rails newする
いつものやつです。
GemfileにOmniAuthを足す
gem 'omniauth'
gem 'omniauth-saml'
initializerを足す
こちらの設定をします。 middlewareでもinitializerでもいいですが、弊社はinitializerにしました。 大体以下のような内容になります。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :developer unless Rails.env.production?
provider :saml,
:issuer => "",
:idp_sso_target_url => "",
:idp_cert => ""
end
それぞれに入れるべきものはG suiteのadmin consoleへ登録するときにわかります。
callbackメソッド作る
先ほど出てきたcallbackメソッドを実装する必要があります。 以下サンプルコードです。
post "/auth/:provider/callback" => "welcome#callback"
def callback
if request.env['omniauth.auth'].extra.response_object.present?
attributes = request.env['omniauth.auth'].extra.response_object.attributes
email = attributes[:email] || ''
first_name = attributes[:first_name] || ''
last_name = attributes[:last_name] || ''
name = last_name + first_name
else
# OmniAuthが提供する開発用の認証方法
# http://localhost:3000/auth/developer
auth = request.env['omniauth.auth']
name = auth[:info][:name]
email = auth[:info][:email]
end
session[:email] = email
redirect_to '/'
end
大体このような感じでヘッダからユーザー情報を取り出せます。 あとはそれをお好みでセッションなどお好きなものに放り込んでそれの有無でログイン状態を判断できます。
albの設定例
awsのalbとoidcを使った設定例です。
oidcクレデンシャルの追加
こちらの
Create credentials => OAuth client IDからクレデンシャルを追加する。- こちらはこちらで上記とは別の権限が必要になります。
Authorized JavaScript originsは適宜設定しましょう。redirect uriはhttps://{{ ホスト名 }}/oauth2/idpresponseを登録しましょう。/oauth2/idpresponseのハンドリングはalbが勝手にやってくれるのでここの実装は必要ありません。
client idとclient secretをメモする。
アプリケーションの公開範囲制限
社内ツールなどは外部に公開する必要がないので、「OAuth同意画面」から「アプリケーションの種類」を「内部」に変更しましょう。
albに設定追加
- awsコンソールから認証を追加したいalbを選び、
Listenersタブを選ぶ。 RulesのところにView/edit rulesとあるので選ぶ。
全てのパスに認証をかけたい場合はこのような感じで設定できます。

- Issuerと3種のendpointはgoogleという事業者で一つなので固定です。
- 厳密には時期によって変わる可能性はあるはず
- これらの情報はgoogleのOpenID Connect Discoveryから取得できます。仕様についてはこちら
ユーザー情報
上記設定のすぐ下にAdvanced settingsというものがあります(Timeoutはテストのため適当に変えたのでデフォルトでいいと思います)。

Scopeというのはsamlでいうattribute mappingsのようなもので、上記のOpenID Connect Discoveryにも scopes_supported という項目で何が指定できるか書いてあります。
最後に、認証が通ったらどこにforwardするか設定します。
実装
amazonによる実装例がこちらにあります。 Rubyで同じ内容に書き起こしましょう。 認証をかけたい機能ではrequestのヘッダを確認して、ユーザー情報が取れない場合は401を返すなり別のところにredirectするなり実装すれば終わりです。
それぞれのまとめ
saml
- albよりは書くことが多い
- とはいえOmniAuthが相当な部分吸収してくれているので大きな手間ではない
- 証明書の期限があるので数年で使えなくなる
- スパンが数年になるとどうしても優先度下がるし忘れそう
- samlはRFCに仕様があるのでamazonにロックインされていないという自己満足
- https://tools.ietf.org/html/rfc7522
- 今回の記事を書くにあたってherokuで立てておさらいしました
alb
- 設定はsamlより楽
- 証明書をアプリで意識しなくてもいい
- ローカルで開発しながら動かすためにはログイン状態にするためちょっとアドホックなコードが必要
- samlの方には開発用のログイン方法がOmniAuthにあるので楽
- もしかしたらいい方法があるのかもしれない
- amazonにロックインされているという被害妄想
- oidc自体はOAuth 2.0をベースにした規格なのでopen
- とはいえ現実的にはamazonから動かすことってあまりなさそう
終わりに
どちらが明確に優れているということは無いと思いますが、仮に自分がまたSSOのアプリを社内向けに用意するとしたらalbを使う気はします。
とはいえalbは当然amazonでしか使えないので、読んでくださっている方の中には使えない場合もあるかもしれません。
どちらの方法でもemailは取れるので(設定は必要)、「認証の結果セッションの中などどこかにemailが入っている」というぐらいまで責務を薄くして作っています。 そうすればあとで切り替えたくなったときも変更は最小限で済むかと思います。
権限についてですが、弊社は今の所誰が何をしたかは記録しているけれど権限制御をガチガチにしているわけではありません。 もしやることになったとしても「Gmailなどの組織図」と「管理ツールの権限の分布」が一致するということは希だと思うので、どのみちそこは手で頑張るしかないと感じています。となると外部に依存するよりは弊社のデータベースなどでemailと紐づけて管理、という形になるのかなとぼんやり思っています。
とはいえ凝りすぎるとSSOの楽さという長所が失われてしまうのでよっぽどの重大な機能(個人情報に関わるなど)でなければ権限を複雑にするつもりは今の所ありません。
何はともあれ、ssoを入れておくと本当に楽です。弊社は少し前に営業の方の入社ラッシュがあったのでそのときに導入できていなかったら多分すごく面倒だったろうなと思っています。
この記事が皆さんのSSO導入の第一歩になればこれに勝る喜びはありません。
ありがとうございました。