Rails+AzureADでSAML認証のSSOを実装する
検索しても詳しいやり方が出てこなかったので、備忘録を兼ねて。
前提
- Azureのアカウントを所持していること
- 無料版の個人アカウントでもok
 
 - Azure ADのテナントが用意されていること
 
Railsアプリの前準備
gem のインストール
SAMLのライブラリとしてruby-saml を、認証情報を扱うのでdotenv-railsをインストール。
gem 'ruby-saml'
gem 'dotenv-rails'
認証周りのコードを書く
ruby-samlはRails用のサンプルコードが用意されているので、それをそのままコピペする。
(↑のコミットログではAccountテーブルを作っているが、実際は不要だった)
Azure ADでSAMLの設定
「Azure Active Directory」->「エンタープライズアプリケーション」->「新しいアプリケーション」->「独自のアプリケーションの作成」と辿る。

任意のアプリ名を入力、ラジオボタンは一番下を選択して作成する。
アプリケーションが作成されたら、「シングルサインオンの設定」->「SAML」を選択し、SAMLによるシングルサインオンのセットアップに入ります。
基本的なSAML構成
ここでは以下の2点を入力する必要がある。
- 識別子(エンティティIID)
 - 応答URL(Assertion Consumer Service URL)
 
これらの値は先ほどコピペしたRails側のコード(app/models/account.rb)でも設定されており、識別子がsettings.issuer、応答URLがsettings.assertion_consumer_service_urlに対応している。
    #SP section
    settings.issuer                         = url_base + "/saml/metadata"
    settings.assertion_consumer_service_url = url_base + "/saml/acs"
今回はlocalhostから確認できればよいので、以下のように入力しておく。
- 識別子 (エンティティ ID)
http://localhost:3000/saml/metadata
 - 応答 URL (Assertion Consumer Service URL)
http://localhost:3000/saml/acs
 
ユーザー属性とクレーム
とりあえず最小限に、メールアドレスと名前のみを要求する。

一意のユーザー識別子はuser.mailと設定。その横に書いてあるnameid-format:emailAddressみたいなコードも確認しておく。(Railsアプリ側にも登録しておく必要があるため)
    settings.name_identifier_format = ENV['NAME_ID_FORMAT']
上記の例だと、NAME_ID_FORMATはこのようなコードになる(最後を合わせてあげれば多分OK)
NAME_ID_FORMAT=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
SAML署名証明書
ここでBase64証明書をダウンロードして、Railsアプリのconfig下に保存する。
    settings.idp_cert = File.read(Rails.root.join('config', 'rails-sso.cer'))
証明書の有効期限が切れたら作り直す必要があるので留意しておきましょう。
(任意のアプリ名)のセットアップ
AzureAD識別子、ログインURL、ログアウトURLが表示されているので、Railsアプリに登録する。SSOがシングルサインオン、SLOがシングルログアウトの略。
    # IdP section
    settings.idp_entity_id = ENV['IDP_ENTITY_ID']    # AzureAD識別子
    settings.idp_sso_target_url = ENV['IDP_SSO_URL'] # ログインURL
    settings.idp_slo_target_url = ENV['IDP_SLO_URL'] # ログアウトURL
最後に「ユーザーとグループ」で自分のアカウントを割り当てて、Azure ADでの設定は完了。
RailsでSSO
http://localhost:3000を開くとLoginリンクが表示される。
上手くいっていたらSAML認証のSSOが実行されるはず。
リポジトリ
Discussion