Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

弊社のSSO事情について

こんにちは、サーバーサイドエンジニアの金澤です。

みなさん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画面から登録しましょう。 このアイコンが目印です。 Screen Shot 2019-05-08 at 18.35.04.png (22.0 kB)

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

Screen Shot 2019-05-08 at 18.33.55.png (84.4 kB)

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

Screen Shot 2019-05-08 at 18.39.08.png (60.8 kB)

entity idはユニークであればなんでもいいのですがurlを入れておくのが分かりやすいし必ずユニークになるので便利かと思います。

有効化する

作った時点ではAppの設定がOff for everyoneなのでonに設定する必要があります。 特定の人にだけ公開したい場合は、下記画面から手で設定することができます。

Screen Shot 2019-05-08 at 20.11.55.png (220.9 kB)

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 urihttps://{{ ホスト名 }}/oauth2/idpresponse を登録しましょう。
    • /oauth2/idpresponse のハンドリングはalbが勝手にやってくれるのでここの実装は必要ありません。
  • client idclient secret をメモする。

アプリケーションの公開範囲制限

社内ツールなどは外部に公開する必要がないので、「OAuth同意画面」から「アプリケーションの種類」を「内部」に変更しましょう。

albに設定追加

  • awsコンソールから認証を追加したいalbを選び、 Listeners タブを選ぶ。
  • Rules のところに View/edit rules とあるので選ぶ。

全てのパスに認証をかけたい場合はこのような感じで設定できます。

Screen Shot 2019-05-08 at 20.00.46.png (218.5 kB)

  • Issuerと3種のendpointはgoogleという事業者で一つなので固定です。
    • 厳密には時期によって変わる可能性はあるはず
  • これらの情報はgoogleのOpenID Connect Discoveryから取得できます。仕様についてはこちら

ユーザー情報

上記設定のすぐ下にAdvanced settingsというものがあります(Timeoutはテストのため適当に変えたのでデフォルトでいいと思います)。

Screen Shot 2018-11-08 at 16.13.03.png (88.1 kB)

Scopeというのはsamlでいうattribute mappingsのようなもので、上記のOpenID Connect Discoveryにも scopes_supported という項目で何が指定できるか書いてあります。

最後に、認証が通ったらどこにforwardするか設定します。

実装

amazonによる実装例がこちらにあります。 Rubyで同じ内容に書き起こしましょう。 認証をかけたい機能ではrequestのヘッダを確認して、ユーザー情報が取れない場合は401を返すなり別のところにredirectするなり実装すれば終わりです。

それぞれのまとめ

saml

  • albよりは書くことが多い
    • とはいえOmniAuthが相当な部分吸収してくれているので大きな手間ではない
  • 証明書の期限があるので数年で使えなくなる
    • スパンが数年になるとどうしても優先度下がるし忘れそう
  • samlはRFCに仕様があるのでamazonにロックインされていないという自己満足

alb

  • 設定はsamlより楽
  • 証明書をアプリで意識しなくてもいい
  • ローカルで開発しながら動かすためにはログイン状態にするためちょっとアドホックなコードが必要
    • samlの方には開発用のログイン方法がOmniAuthにあるので楽
    • もしかしたらいい方法があるのかもしれない
  • amazonにロックインされているという被害妄想
    • oidc自体はOAuth 2.0をベースにした規格なのでopen
    • とはいえ現実的にはamazonから動かすことってあまりなさそう

終わりに

どちらが明確に優れているということは無いと思いますが、仮に自分がまたSSOのアプリを社内向けに用意するとしたらalbを使う気はします。

とはいえalbは当然amazonでしか使えないので、読んでくださっている方の中には使えない場合もあるかもしれません。

どちらの方法でもemailは取れるので(設定は必要)、「認証の結果セッションの中などどこかにemailが入っている」というぐらいまで責務を薄くして作っています。 そうすればあとで切り替えたくなったときも変更は最小限で済むかと思います。

権限についてですが、弊社は今の所誰が何をしたかは記録しているけれど権限制御をガチガチにしているわけではありません。 もしやることになったとしても「Gmailなどの組織図」と「管理ツールの権限の分布」が一致するということは希だと思うので、どのみちそこは手で頑張るしかないと感じています。となると外部に依存するよりは弊社のデータベースなどでemailと紐づけて管理、という形になるのかなとぼんやり思っています。

とはいえ凝りすぎるとSSOの楽さという長所が失われてしまうのでよっぽどの重大な機能(個人情報に関わるなど)でなければ権限を複雑にするつもりは今の所ありません。

何はともあれ、ssoを入れておくと本当に楽です。弊社は少し前に営業の方の入社ラッシュがあったのでそのときに導入できていなかったら多分すごく面倒だったろうなと思っています。

この記事が皆さんのSSO導入の第一歩になればこれに勝る喜びはありません。

ありがとうございました。