こんにちは!kossyです!
さて、今回はdevise_saml_authenticatable + LINE WORKS で SAMLを使ったSSOを実装してみたので、備忘録としてブログに残してみたいと思います。
偉大なる本家レポジトリ
環境
Ruby 2.6.8
Rails 6.0.4.1
MacOS Catalina
Railsアプリの新規作成
rails newでサンプルアプリを作ります。
$ bundle exec rails new devise_saml_sp -d postgresql -T $ cd devise_saml_sp $ bundle exec rails db:create
Gemの導入
deviseとdevise_saml_authenticatableを導入します。
# Gemfile gem 'devise' gem 'devise_saml_authenticatable'
その後bundle installを実行します。
$ bundle instal
DeviseのインストールとUserモデルの作成・編集
$ bundle exec rails g devise:install
$ bundle exec rails g devise user
作成されたUserモデルに修正を加えます。
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :saml_authenticatable # saml_authenticatableを追加 end
ビューにフラッシュメッセージを表示するための記述を追記します。
<body> # ここから追加 <% if flash[:notice] %> <div> <%= flash[:notice] %> </div> <% end %> <% if flash[:alert] %> <div> <%= flash[:alert] %> </div> <% end %> # ここまで <%= yield %> </body>
config/devise.rbにsamlの設定を追加
config.saml_configure do |settings| # assertion_consumer_service_url is required starting with ruby-saml 1.4.3: https://github.com/onelogin/ruby-saml#updating-from-142-to-143 settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth" settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" settings.issuer = "http://localhost:3000/users/saml/metadata" settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" settings.idp_entity_id = "https://auth.worksmobile.com/saml2/your_group_name" settings.idp_slo_service_url = "https://auth.worksmobile.com/saml2/idp/your_group_name/logout" settings.idp_sso_service_url = "https://auth.worksmobile.com/saml2/idp/your_group_name" settings.idp_cert_fingerprint = "後ほど設定します" settings.idp_cert_fingerprint_algorithm = XMLSecurity::Document::SHA256 end
仮のトップページの作成
$ rails g controller home index
# app/views/home/index.html.erb <% if current_user.present? %> <p>ID: <%= current_user.email %></p> <p><%= link_to "ログアウト", destroy_user_session_path, method: :delete %></p> <% else %> <p>ログインしてね☆</p> <p><%= link_to "メアドとパスワードでログイン", new_user_session_path %></p> <p><%= link_to "SAMLでログイン", new_user_sso_session_path %></p> <% end %>
# config/routes.rb Rails.application.routes.draw do root 'home#index' # 追加 # 省略 end
config/attribute-map.ymlの作成
SAMLの値とUserモデルの値をマッピングするためのconfigファイルを作成します。
"urn:mace:dir:attribute-def:email": "email"
LINE WORKSにSAML Appsを新規登録
LINE WORKSでSAML2.0によるSSO機能を使うには、有料アカウントで登録する必要があります。(2021年10月時点。ライトプラン・ユーザー一人当たり月額360円)
360円は自己投資だと思うようにしましょう(技術書よりは全然安いし、、、)
料金表はこちらです。
まずは無料でアカウントを作成し、その後にライトプランにアップグレードする必要があります。
無事課金し終えてライトプランにアップグレードできましたら、LINE WORKSのデベロッパーコンソールからSSO機能を利用するアプリの登録を行う必要があります。
・Application Nameにはsaml-sp
・ACS URLには http://localhost:3000/users/saml/auth
・SP Issuer(Entity ID)には http://localhost:3000/users/saml/metadata
を入力してください。
「次へ」をクリックし、以下の表示が出れば設定成功です。

登録後、以下URLにアクセスすると、
https://developers.worksmobile.com/jp/console/idp/saml/view

先ほど追加したサービスが表示されていると思いますので、まずは使用状態を「無効」から「有効」に切り替えます。
「変更」をクリックすると、以下のモーダルが表示されますので、ラジオボタンの「有効」をクリックし、「保存」をクリックすると、使用状態を切り替えられます。
次に、「LINE WORKS Identity Provider情報」をクリックすると、SSO URL と Response Issuer の確認と Certificate のダウンロードが行えますので、
SSO URL と Response Issuer は一旦どこかにコピペしておき、 Certificateのダウンロードを行ってください。

ダウンロードしたCertificateを使って、以下コマンドを叩いてフィンガープリントを取得してください。
$ openssl x509 -text -noout -in ~/Downloads/<your file name> -fingerprint -sha256
出力された値はおそらく以下のような感じになるはず。
SHA256 Fingerprint=59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM
この出力された値を settings.idp_cert_fingerprint に設定してください。
config.saml_configure do |settings| # assertion_consumer_service_url is required starting with ruby-saml 1.4.3: https://github.com/onelogin/ruby-saml#updating-from-142-to-143 settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth" settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" settings.issuer = "http://localhost:3000/users/saml/metadata" settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" settings.idp_entity_id = "https://auth.worksmobile.com/saml2/your_group_name" settings.idp_slo_service_url = "https://auth.worksmobile.com/saml2/idp/your_group_name/logout" settings.idp_sso_service_url = "https://auth.worksmobile.com/saml2/idp/your_group_name" settings.idp_cert_fingerprint = "59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM:59:10:LO:D0:9L:31:PO:AM" settings.idp_cert_fingerprint_algorithm = XMLSecurity::Document::SHA256 end
これで準備完了です。
動作確認
まず、Userモデルにレコードを追加します。この時、LINE WORKSのIDと同じ値をemailに設定する必要があります。
$ rails c $ User.create!(email: "your_line_works_id@your_group_name", password: "1234test")
その後、トップページの「SAMLでログイン」のリンクを踏んでください。諸々の設定が正しければLINE WORKSのログイン画面に遷移します。
IDとPASSWORDを入力してログインを押すと、自分のアプリにリダイレクトされます。
ログアウトボタンを押すと正常にログアウトできます。
メールアドレスとパスワードでのログインも試してみましょう。
正しいメールアドレスとパスワードであればフラッシュメッセージと共にトップページにリダイレクトされるはずです。
ログインした状態で /users/saml/sign_in にアクセスすると以下の表示になります。
既にログイン済みである旨のフラッシュメッセージが表示されました。
まとめ
メールアドレス + パスワードでのログインとSAMLによるSSOを共存させることを比較的簡単に行えました。
本来はUserモデルに紐づく形でSAMLの設定を保存するモデルを追加するのが筋だと思いますが、今回は「まずは動かす」ところに重点を置いてみました。
近いうちにコードもGem内部のコードも読んでみようと思います。
大いに参考にさせていただいたサイト
素晴らしいコンテンツの提供、誠にありがとうございます。
devise_saml_authenticatable/README.md at master · apokalipto/devise_saml_authenticatable · GitHub
翻訳:Ruby on RailsでDevise・SAML認証(LINE WORKS) - Qiita