m_shige1979のささやかな抵抗と欲望の日々

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

saml認証をruby-samlライブラリを利用して自前のサーバをSPサーバとして構築

基本、前回の続きみたいなもの

m-shige1979.hatenablog.com

前回は以下のような感じで一部の処理をcognitoが一部代行

IDP側の処理はIDP側で制御するがSP側の処理でどんなデータを送信していたかなどが全く不明のため、 作成することにした

1から作成するにしてもデータ形式とかの仕様がわからない

のでライブラリを元に追いかけることにする 今回は「ruby-saml」の情報を元にexampleを探して作成

流れ

  1. SP側のエンティティIDとACS URLを決める
  2. IDPにエンティティIDとACS URLを設定し、IDPのメタデータを取得
  3. 環境変数に設定情報のデータを定義
  4. コード改修
  5. テスト

手順

1. SP側のエンティティIDとACS URLを決める

No 項目 設定値
1 エンティティID http://localhost:3000/saml/metadata
2 ASC http://localhost:3000/saml/acs

2. IDPにエンティティIDとACS URLを設定し、IDPのメタデータを取得

3. 環境変数に設定情報のデータを定義

.env.dev

SP_ISSUER  = SP用のエンティティID(例:http://localhost:3000/saml/metadata)
SP_ACS_URL = SP用のIDPの認証結果を受け取るコールバック(例:http://localhost:3000/saml/acs)
SP_LOGOUT  = SP用のログアウト用のエンドポイント(例:http://localhost:3000/saml/logout)
SP_CERT_KEY= SP用の証明書に利用したキー(例:saml/sp/private.pem)
SP_CERT    = SP用の証明書(例:saml/sp/sp.cert)

IDP_ISSUER = IDPのエンティティID(メタデータに記載)
IDP_SSO_URL= IDPのシングルサインオンURL(メタデータに記載)
IDP_SLO_URL= IDPのシングルサインアウトURL(メタデータに記載)
IDP_CERT   = IDPの証明書(例:saml/idp/idp.cert)
IDP_ID_FORMT  = IDPのIDフォーマット(例:urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress)

4. コード改修

app/models/account.rb

class Account < ActiveRecord::Base
    def self.get_saml_settings(url_base)
      # this is just for testing purposes.
      # should retrieve SAML-settings based on subdomain, IP-address, NameID or similar
      settings = OneLogin::RubySaml::Settings.new
  
      # When disabled, saml validation errors will raise an exception.
      settings.soft = true
  
      #SP section
      settings.issuer                         = ENV["SP_ISSUER"]
      settings.assertion_consumer_service_url = ENV["SP_ACS_URL"]
      settings.assertion_consumer_logout_service_url = ENV["SP_LOGOUT"]
      settings.certificate = File.read(Rails.root.join('config', ENV["SP_CERT"]))
      settings.private_key = File.read(Rails.root.join('config', ENV["SP_CERT_KEY"]))
  
      # IdP section
      settings.idp_entity_id                  = ENV["IDP_ISSUER"]
      settings.idp_sso_target_url             = ENV["IDP_SSO_URL"]
      settings.idp_slo_target_url             = ENV["IDP_SLO_URL"]
      settings.idp_cert                       = File.read(Rails.root.join('config', ENV["IDP_CERT"]))
    
      settings.name_identifier_format         = ENV["IDP_ID_FORMT"]
  
      # Security section
      settings.security[:authn_requests_signed] = true
      settings.security[:logout_requests_signed] = true
      settings.security[:logout_responses_signed] = true
      settings.security[:metadata_signed] = true
      settings.security[:digest_method] = XMLSecurity::Document::SHA256
      settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
  
      settings
    end
  end

※証明書は任意の場所へ配置しておくこと

結果

SAMLリクエストとSAMLレスポンスについて

SAMLリクエス

まずはXML形式で作成するみたい

<samlp:AuthnRequest 
    AssertionConsumerServiceURL='http://localhost:3000/saml/acs' 
    Destination='IDP側のリクエストURL' 
    ID='任意のリクエスト文字列' 
    IssueInstant='リクスト日時(UTC)' Version='2.0' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'>
    <saml:Issuer>http://localhost:3000/saml/metadata</saml:Issuer>
    <samlp:NameIDPolicy AllowCreate='true' Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'/>
</samlp:AuthnRequest>


base64で暗号化しているみたいです

[IDPのログインURL]?SAMLRequest=fZLLTsMwEEV%2FJTuvUttJA9RqKkVESJUA...

SAMLレスポンスも同じようにXML形式の物が暗号化されてくるので デコードしてから検証する感じ

成果物

github.com

環境変数とかは自前の環境のものを利用すること ※元々のサンプルとはバージョンが違うためところところ設定を追記しています

参考

GitHub - onelogin/ruby-saml: SAML SSO for Ruby
GitHub - onelogin/ruby-saml-example: Ruby example of how to use the ruby-saml gem
ruby-saml を使ったOneLogin での SAML 認証の処理シーケンスを追う - Qiita
Rails+AzureADでSAML認証のSSOを実装する