ruby-saml 1.8.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +25 -0
  3. data/{changelog.md → CHANGELOG.md} +66 -1
  4. data/README.md +365 -209
  5. data/UPGRADING.md +149 -0
  6. data/lib/onelogin/ruby-saml/attribute_service.rb +1 -1
  7. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  8. data/lib/onelogin/ruby-saml/authrequest.rb +25 -9
  9. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +285 -184
  10. data/lib/onelogin/ruby-saml/logging.rb +4 -1
  11. data/lib/onelogin/ruby-saml/logoutrequest.rb +25 -10
  12. data/lib/onelogin/ruby-saml/logoutresponse.rb +33 -17
  13. data/lib/onelogin/ruby-saml/metadata.rb +62 -17
  14. data/lib/onelogin/ruby-saml/response.rb +89 -45
  15. data/lib/onelogin/ruby-saml/saml_message.rb +17 -8
  16. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +124 -43
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +38 -11
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +42 -19
  20. data/lib/onelogin/ruby-saml/utils.rb +94 -12
  21. data/lib/onelogin/ruby-saml/version.rb +1 -1
  22. data/lib/xml_security.rb +44 -19
  23. data/ruby-saml.gemspec +16 -8
  24. metadata +44 -282
  25. data/.travis.yml +0 -26
  26. data/test/certificates/certificate1 +0 -12
  27. data/test/certificates/certificate_without_head_foot +0 -1
  28. data/test/certificates/formatted_certificate +0 -14
  29. data/test/certificates/formatted_chained_certificate +0 -42
  30. data/test/certificates/formatted_private_key +0 -12
  31. data/test/certificates/formatted_rsa_private_key +0 -12
  32. data/test/certificates/invalid_certificate1 +0 -1
  33. data/test/certificates/invalid_certificate2 +0 -1
  34. data/test/certificates/invalid_certificate3 +0 -12
  35. data/test/certificates/invalid_chained_certificate1 +0 -1
  36. data/test/certificates/invalid_private_key1 +0 -1
  37. data/test/certificates/invalid_private_key2 +0 -1
  38. data/test/certificates/invalid_private_key3 +0 -10
  39. data/test/certificates/invalid_rsa_private_key1 +0 -1
  40. data/test/certificates/invalid_rsa_private_key2 +0 -1
  41. data/test/certificates/invalid_rsa_private_key3 +0 -10
  42. data/test/certificates/ruby-saml-2.crt +0 -15
  43. data/test/certificates/ruby-saml.crt +0 -14
  44. data/test/certificates/ruby-saml.key +0 -15
  45. data/test/idp_metadata_parser_test.rb +0 -579
  46. data/test/logging_test.rb +0 -62
  47. data/test/logout_requests/invalid_slo_request.xml +0 -6
  48. data/test/logout_requests/slo_request.xml +0 -4
  49. data/test/logout_requests/slo_request.xml.base64 +0 -1
  50. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  51. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  52. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  53. data/test/logout_responses/logoutresponse_fixtures.rb +0 -67
  54. data/test/logoutrequest_test.rb +0 -226
  55. data/test/logoutresponse_test.rb +0 -402
  56. data/test/metadata/idp_descriptor.xml +0 -26
  57. data/test/metadata/idp_descriptor_2.xml +0 -56
  58. data/test/metadata/idp_descriptor_3.xml +0 -14
  59. data/test/metadata/idp_descriptor_4.xml +0 -72
  60. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  61. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  62. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  63. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  64. data/test/metadata/idp_multiple_descriptors.xml +0 -53
  65. data/test/metadata/no_idp_descriptor.xml +0 -21
  66. data/test/metadata_test.rb +0 -331
  67. data/test/request_test.rb +0 -323
  68. data/test/response_test.rb +0 -1586
  69. data/test/responses/adfs_response_sha1.xml +0 -46
  70. data/test/responses/adfs_response_sha256.xml +0 -46
  71. data/test/responses/adfs_response_sha384.xml +0 -46
  72. data/test/responses/adfs_response_sha512.xml +0 -46
  73. data/test/responses/adfs_response_xmlns.xml +0 -45
  74. data/test/responses/attackxee.xml +0 -13
  75. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  76. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  77. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  78. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  84. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  85. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  86. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  87. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  88. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  89. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  90. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  91. data/test/responses/invalids/no_id.xml.base64 +0 -1
  92. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  93. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  94. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  95. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  96. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  97. data/test/responses/invalids/no_status.xml.base64 +0 -1
  98. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  99. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  100. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  101. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  102. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  103. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  104. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  105. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  106. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  107. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  108. data/test/responses/no_signature_ns.xml +0 -48
  109. data/test/responses/open_saml_response.xml +0 -56
  110. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  111. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  112. data/test/responses/response_double_status_code.xml.base64 +0 -1
  113. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  114. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  115. data/test/responses/response_eval.xml +0 -7
  116. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  117. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  118. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  119. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  120. data/test/responses/response_unsigned_xml_base64 +0 -1
  121. data/test/responses/response_with_ampersands.xml +0 -139
  122. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  123. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  124. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  125. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  126. data/test/responses/response_with_retrieval_method.xml +0 -26
  127. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  128. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  129. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  130. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  131. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  132. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  133. data/test/responses/response_without_attributes.xml.base64 +0 -79
  134. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  135. data/test/responses/response_wrapped.xml.base64 +0 -150
  136. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  137. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  138. data/test/responses/signed_nameid_in_atts.xml +0 -47
  139. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  140. data/test/responses/simple_saml_php.xml +0 -71
  141. data/test/responses/starfield_response.xml.base64 +0 -1
  142. data/test/responses/test_sign.xml +0 -43
  143. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  144. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  148. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  149. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  150. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  151. data/test/responses/valid_response.xml.base64 +0 -1
  152. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  153. data/test/saml_message_test.rb +0 -56
  154. data/test/settings_test.rb +0 -301
  155. data/test/slo_logoutrequest_test.rb +0 -448
  156. data/test/slo_logoutresponse_test.rb +0 -199
  157. data/test/test_helper.rb +0 -327
  158. data/test/utils_test.rb +0 -254
  159. data/test/xml_security_test.rb +0 -421
data/README.md CHANGED
@@ -1,127 +1,90 @@
1
- # Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master%0A)](https://coveralls.io/r/onelogin/ruby-saml?branch=master%0A) [![Gem Version](https://badge.fury.io/rb/ruby-saml.svg)](http://badge.fury.io/rb/ruby-saml)
1
+ # Ruby SAML
2
+ [![Build Status](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml/badge.svg?query=branch%3Amaster)](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml?query=branch%3Amaster)
3
+ [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
2
4
 
3
- ## Updating from 1.7.X to 1.8.0
4
- On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState param will not generate a URL with an empty RelayState parameter anymore. It also changes the invalid audience error message.
5
+ Ruby SAML minor and tiny versions may introduce breaking changes. Please read
6
+ [UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
5
7
 
6
- ## Updating from 1.6.0 to 1.7.0
7
-
8
- Version `1.7.0` is a recommended update for all Ruby SAML users as it includes a fix for the [CVE-2017-11428](https://www.cvedetails.com/cve/CVE-2017-11428/) vulnerability.
9
-
10
- ## Updating from 1.5.0 to 1.6.0
11
-
12
- Version `1.6.0` changes the preferred way to construct instances of `Logoutresponse` and `SloLogoutrequest`. Previously the _SAMLResponse_, _RelayState_, and _SigAlg_ parameters of these message types were provided via the constructor's `options[:get_params]` parameter. Unfortunately this can result in incompatibility with other SAML implementations; signatures are specified to be computed based on the _sender's_ URI-encoding of the message, which can differ from that of Ruby SAML. In particular, Ruby SAML's URI-encoding does not match that of Microsoft ADFS, so messages from ADFS can fail signature validation.
13
-
14
- The new preferred way to provide _SAMLResponse_, _RelayState_, and _SigAlg_ is via the `options[:raw_get_params]` parameter. For example:
15
-
16
- ```ruby
17
- # In this example `query_params` is assumed to contain decoded query parameters,
18
- # and `raw_query_params` is assumed to contain encoded query parameters as sent by the IDP.
19
- settings = {
20
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
21
- settings.soft = false
22
- }
23
- options = {
24
- get_params: {
25
- "Signature" => query_params["Signature"],
26
- },
27
- raw_get_params: {
28
- "SAMLRequest" => raw_query_params["SAMLRequest"],
29
- "SigAlg" => raw_query_params["SigAlg"],
30
- "RelayState" => raw_query_params["RelayState"],
31
- },
32
- }
33
- slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
34
- raise "Invalid Logout Request" unless slo_logout_request.is_valid?
35
- ```
36
-
37
- The old form is still supported for backward compatibility, but all Ruby SAML users should prefer `options[:raw_get_params]` where possible to ensure compatibility with other SAML implementations.
38
-
39
- ## Updating from 1.4.2 to 1.4.3
40
-
41
- Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
42
- The 'Recipient' value is compared with the settings.assertion_consumer_service_url
43
- value.
44
- If you want to skip that validation, add the :skip_recipient_check option to the
45
- initialize method of the Response object.
46
-
47
- Parsing metadata that contains more than one certificate will propagate the
48
- idp_cert_multi property rather than idp_cert. See [signature validation
49
- section](#signature-validation) for details.
50
-
51
- ## Updating from 1.3.x to 1.4.X
52
-
53
- Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
54
-
55
- ## Updating from 1.2.x to 1.3.X
56
-
57
- Version `1.3.0` is a recommended update for all Ruby SAML users as it includes security fixes. It adds security improvements in order to prevent Signature wrapping attacks. [CVE-2016-5697](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5697)
58
-
59
- ## Updating from 1.1.x to 1.2.X
60
-
61
- Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom, refactor error handling and some minor improvements
62
-
63
- There is no compatibility issue detected.
64
-
65
- For more details, please review [the changelog](changelog.md).
66
-
67
- ## Updating from 1.0.x to 1.1.X
68
-
69
- Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
70
-
71
- ## Updating from 0.9.x to 1.0.X
72
-
73
- Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
8
+ ## Overview
74
9
 
75
- Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
10
+ The Ruby SAML library is for implementing the client side of a SAML authorization,
11
+ i.e. it provides a means for managing authorization initialization and confirmation
12
+ requests from identity providers.
76
13
 
77
- ### Important Changes
78
- Please note the `get_idp_metadata` method raises an exception when it is not able to fetch the idp metadata, so review your integration if you are using this functionality.
14
+ SAML authorization is a two step process and you are expected to implement support for both.
79
15
 
80
- ## Updating from 0.8.x to 0.9.x
81
- Version `0.9` adds many new features and improvements.
16
+ We created a demo project for Rails 4 that uses the latest version of this library:
17
+ [ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
82
18
 
83
- ## Updating from 0.7.x to 0.8.x
84
- Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
19
+ ### Supported Ruby Versions
85
20
 
86
- ## Overview
21
+ The following Ruby versions are covered by CI testing:
87
22
 
88
- The Ruby SAML library is for implementing the client side of a SAML authorization, i.e. it provides a means for managing authorization initialization and confirmation requests from identity providers.
89
-
90
- SAML authorization is a two step process and you are expected to implement support for both.
23
+ * 2.1.x
24
+ * 2.2.x
25
+ * 2.3.x
26
+ * 2.4.x
27
+ * 2.5.x
28
+ * 2.6.x
29
+ * 2.7.x
30
+ * 3.0.x
31
+ * JRuby 9.1.x
32
+ * JRuby 9.2.x
33
+ * TruffleRuby (latest)
91
34
 
92
- We created a demo project for Rails4 that uses the latest version of this library: [ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
35
+ In addition, the following may work but are untested:
93
36
 
94
- ### Supported versions of Ruby
95
37
  * 1.8.7
96
38
  * 1.9.x
97
39
  * 2.0.x
98
- * 2.1.x
99
- * 2.2.x
100
- * 2.3.x
101
- * 2.4.x
102
- * JRuby 1.7.19
103
- * JRuby 9.0.0.0
40
+ * JRuby 1.7.x
41
+ * JRuby 9.0.x
104
42
 
105
43
  ## Adding Features, Pull Requests
44
+
106
45
  * Fork the repository
107
46
  * Make your feature addition or bug fix
108
47
  * Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
109
- * Ensure all tests pass.
48
+ * Ensure all tests pass by running `bundle exec rake test`.
110
49
  * Do not change rakefile, version, or history.
111
50
  * Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
112
51
 
113
52
  ## Security Guidelines
114
53
 
115
- If you believe you have discovered a security vulnerability in this gem, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
54
+ If you believe you have discovered a security vulnerability in this gem, please report it
55
+ at https://www.onelogin.com/security with a description. We follow responsible disclosure
56
+ guidelines, and will work with you to quickly find a resolution.
57
+
58
+ ### Security Warning
59
+
60
+ Some tools may incorrectly report ruby-saml is a potential security vulnerability.
61
+ ruby-saml depends on Nokogiri, and it's possible to use Nokogiri in a dangerous way
62
+ (by enabling its DTDLOAD option and disabling its NONET option).
63
+ This dangerous Nokogiri configuration, which is sometimes used by other components,
64
+ can create an XML External Entity (XXE) vulnerability if the XML data is not trusted.
65
+ However, ruby-saml never enables this dangerous Nokogiri configuration;
66
+ ruby-saml never enables DTDLOAD, and it never disables NONET.
67
+
68
+ The OneLogin::RubySaml::IdpMetadataParser class does not validate in any way the URL
69
+ that is introduced in order to be parsed.
70
+
71
+ Usually the same administrator that handles the Service Provider also sets the URL to
72
+ the IdP, which should be a trusted resource.
73
+
74
+ But there are other scenarios, like a SAAS app where the administrator of the app
75
+ delegates this functionality to other users. In this case, extra precaution should
76
+ be taken in order to validate such URL inputs and avoid attacks like SSRF.
116
77
 
117
78
  ## Getting Started
118
- In order to use the toolkit you will need to install the gem (either manually or using Bundler), and require the library in your Ruby application:
79
+
80
+ In order to use Ruby SAML you will need to install the gem (either manually or using Bundler),
81
+ and require the library in your Ruby application:
119
82
 
120
83
  Using `Gemfile`
121
84
 
122
85
  ```ruby
123
86
  # latest stable
124
- gem 'ruby-saml', '~> 1.0.0'
87
+ gem 'ruby-saml', '~> 1.11.0'
125
88
 
126
89
  # or track master for bleeding-edge
127
90
  gem 'ruby-saml', :github => 'onelogin/ruby-saml'
@@ -133,7 +96,8 @@ Using RubyGems
133
96
  gem install ruby-saml
134
97
  ```
135
98
 
136
- When requiring the gem, you can add the whole toolkit
99
+ You may require the entire Ruby SAML gem:
100
+
137
101
  ```ruby
138
102
  require 'onelogin/ruby-saml'
139
103
  ```
@@ -146,7 +110,9 @@ require 'onelogin/ruby-saml/authrequest'
146
110
 
147
111
  ### Installation on Ruby 1.8.7
148
112
 
149
- This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6. When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri prior to 1.6 is installed or specified if it hasn't been already.
113
+ This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6.
114
+ When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri
115
+ prior to 1.6 is installed or specified if it hasn't been already.
150
116
 
151
117
  Using `Gemfile`
152
118
 
@@ -170,12 +136,15 @@ To override the default behavior and control the destination of log messages, pr
170
136
  a ruby Logger object to the gem's logging singleton:
171
137
 
172
138
  ```ruby
173
- OneLogin::RubySaml::Logging.logger = Logger.new(File.open('/var/log/ruby-saml.log', 'w'))
139
+ OneLogin::RubySaml::Logging.logger = Logger.new('/var/log/ruby-saml.log')
174
140
  ```
175
141
 
176
142
  ## The Initialization Phase
177
143
 
178
- This is the first request you will get from the identity provider. It will hit your application at a specific URL that you've announced as your SAML initialization point. The response to this initialization is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now):
144
+ This is the first request you will get from the identity provider. It will hit your application
145
+ at a specific URL that you've announced as your SAML initialization point. The response to
146
+ this initialization is a redirect back to the identity provider, which can look something
147
+ like this (ignore the saml_settings method call for now):
179
148
 
180
149
  ```ruby
181
150
  def init
@@ -184,7 +153,21 @@ def init
184
153
  end
185
154
  ```
186
155
 
187
- Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption. This can look something like this (the `authorize_success` and `authorize_failure` methods are specific to your application):
156
+ If the SP knows who should be authenticated in the IdP, then can provide that info as follows:
157
+
158
+ ```ruby
159
+ def init
160
+ request = OneLogin::RubySaml::Authrequest.new
161
+ saml_settings.name_identifier_value_requested = "testuser@example.com"
162
+ saml_settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
163
+ redirect_to(request.create(saml_settings))
164
+ end
165
+ ```
166
+
167
+ Once you've redirected back to the identity provider, it will ensure that the user has been
168
+ authorized and redirect back to your application for final consumption.
169
+ This can look something like this (the `authorize_success` and `authorize_failure`
170
+ methods are specific to your application):
188
171
 
189
172
  ```ruby
190
173
  def consume
@@ -197,20 +180,24 @@ def consume
197
180
  session[:attributes] = response.attributes
198
181
  else
199
182
  authorize_failure # This method shows an error message
183
+ # List of errors is available in response.errors array
200
184
  end
201
185
  end
202
186
  ```
203
187
 
204
- In the above there are a few assumptions, one being that `response.nameid` is an email address. This is all handled with how you specify the settings that are in play via the `saml_settings` method. That could be implemented along the lines of this:
188
+ In the above there are a few assumptions, one being that `response.nameid` is an email address.
189
+ This is all handled with how you specify the settings that are in play via the `saml_settings` method.
190
+ That could be implemented along the lines of this:
205
191
 
206
192
  ```
207
193
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
208
194
  response.settings = saml_settings
209
195
  ```
210
196
 
211
- If the assertion of the SAMLResponse is not encrypted, you can initialize the Response without the `:settings` parameter and set it later.
212
- If the SAMLResponse contains an encrypted assertion, you need to provide the settings in the
213
- initialize method in order to obtain the decrypted assertion, using the service provider private key in order to decrypt.
197
+ If the assertion of the SAMLResponse is not encrypted, you can initialize the Response
198
+ without the `:settings` parameter and set it later. If the SAMLResponse contains an encrypted
199
+ assertion, you need to provide the settings in the initialize method in order to obtain the
200
+ decrypted assertion, using the service provider private key in order to decrypt.
214
201
  If you don't know what expect, always use the former (set the settings on initialize).
215
202
 
216
203
  ```ruby
@@ -218,10 +205,12 @@ def saml_settings
218
205
  settings = OneLogin::RubySaml::Settings.new
219
206
 
220
207
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
221
- settings.issuer = "http://#{request.host}/saml/metadata"
208
+ settings.sp_entity_id = "http://#{request.host}/saml/metadata"
222
209
  settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
223
- settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
224
- settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
210
+ settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
211
+ settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
212
+ settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
213
+ settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
225
214
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
226
215
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
227
216
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@@ -234,24 +223,30 @@ def saml_settings
234
223
  "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
235
224
  ]
236
225
 
237
- # Optional bindings (defaults to Redirect for logout POST for acs)
238
- settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
239
- settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
226
+ # Optional bindings (defaults to Redirect for logout POST for ACS)
227
+ settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
228
+ settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
240
229
 
241
230
  settings
242
231
  end
243
232
  ```
244
233
 
245
- Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`. For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation` validations by initializing the response with different options:
234
+ The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
235
+
236
+ Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`.
237
+ For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation`
238
+ validations by initializing the response with different options:
246
239
 
247
240
  ```ruby
248
241
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
249
242
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
250
243
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
251
- response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doens't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
244
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doesn't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
245
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
252
246
  ```
253
247
 
254
- All that's left is to wrap everything in a controller and reference it in the initialization and consumption URLs in OneLogin. A full controller example could look like this:
248
+ All that's left is to wrap everything in a controller and reference it in the initialization and
249
+ consumption URLs in OneLogin. A full controller example could look like this:
255
250
 
256
251
  ```ruby
257
252
  # This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application.
@@ -272,6 +267,7 @@ class SamlController < ApplicationController
272
267
  session[:attributes] = response.attributes
273
268
  else
274
269
  authorize_failure # This method shows an error message
270
+ # List of errors is available in response.errors array
275
271
  end
276
272
  end
277
273
 
@@ -281,8 +277,8 @@ class SamlController < ApplicationController
281
277
  settings = OneLogin::RubySaml::Settings.new
282
278
 
283
279
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
284
- settings.issuer = "http://#{request.host}/saml/metadata"
285
- settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
280
+ settings.sp_entity_id = "http://#{request.host}/saml/metadata"
281
+ settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
286
282
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
287
283
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
288
284
 
@@ -303,46 +299,60 @@ class SamlController < ApplicationController
303
299
  end
304
300
  ```
305
301
 
302
+ ## Signature Validation
306
303
 
307
- ## Signature validation
304
+ Ruby SAML allows different ways to validate the signature of the SAMLResponse:
305
+ - You can provide the IdP X.509 public certificate at the `idp_cert` setting.
306
+ - You can provide the IdP X.509 public certificate in fingerprint format using the
307
+ `idp_cert_fingerprint` setting parameter and additionally the `idp_cert_fingerprint_algorithm` parameter.
308
308
 
309
- On the ruby-saml toolkit there are different ways to validate the signature of the SAMLResponse:
310
- - You can provide the IdP x509 public certificate at the 'idp_cert' setting.
311
- - You can provide the IdP x509 public certificate in fingerprint format using the 'idp_cert_fingerprint' setting parameter and additionally the 'idp_cert_fingerprint_algorithm' parameter.
309
+ When validating the signature of redirect binding, the fingerprint is useless and the certificate
310
+ of the IdP is required in order to execute the validation. You can pass the option
311
+ `:relax_signature_validation` to `SloLogoutrequest` and `Logoutresponse` if want to avoid signature
312
+ validation if no certificate of the IdP is provided.
312
313
 
313
- When validating the signature of redirect binding, the fingerprint is useless and the certficate of the IdP is required in order to execute the validation.
314
- You can pass the option :relax_signature_validation to SloLogoutrequest and Logoutresponse if want to avoid signature validation if no certificate of the IdP is provided.
314
+ In production also we highly recommend to register on the settings the IdP certificate instead
315
+ of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision
316
+ attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism,
317
+ we maintain it for compatibility and also to be used on test environment.
315
318
 
316
- In some scenarios the IdP uses different certificates for signing/encryption, or is under key rollover phase and more than one certificate is published on IdP metadata.
319
+ ## Handling Multiple IdP Certificates
317
320
 
318
- In order to handle that the toolkit offers the 'idp_cert_multi' parameter.
319
- When used, 'idp_cert' and 'idp_cert_fingerprint' values are ignored.
321
+ If the IdP metadata XML includes multiple certificates, you may specify the `idp_cert_multi`
322
+ parameter. When used, the `idp_cert` and `idp_cert_fingerprint` parameters are ignored.
323
+ This is useful in the following scenarios:
320
324
 
321
- That 'idp_cert_multi' must be a Hash as follows:
325
+ * The IdP uses different certificates for signing versus encryption.
326
+ * The IdP is undergoing a key rollover and is publishing the old and new certificates in parallel.
327
+
328
+ The `idp_cert_multi` must be a `Hash` as follows. The `:signing` and `:encryption` arrays below,
329
+ add the IdP X.509 public certificates which were published in the IdP metadata.
330
+
331
+ ```ruby
322
332
  {
323
333
  :signing => [],
324
334
  :encryption => []
325
335
  }
326
-
327
- And on 'signing' and 'encryption' arrays, add the different IdP x509 public certificates published on the IdP metadata.
328
-
336
+ ```
329
337
 
330
338
  ## Metadata Based Configuration
331
339
 
332
- The method above requires a little extra work to manually specify attributes about the IdP. (And your SP application) There's an easier method -- use a metadata exchange. Metadata is just an XML file that defines the capabilities of both the IdP and the SP application. It also contains the X.509 public
333
- key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata.
340
+ The method above requires a little extra work to manually specify attributes about both the IdP and your SP application.
341
+ There's an easier method: use a metadata exchange. Metadata is an XML file that defines the capabilities of both the IdP
342
+ and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship.
343
+ The IdP administrator can also configure custom settings for an SP based on the metadata.
334
344
 
335
- Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings without further ado.
345
+ Using `IdpMetadataParser#parse_remote`, the IdP metadata will be added to the settings.
336
346
 
337
347
  ```ruby
338
348
  def saml_settings
339
349
 
340
350
  idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
341
- # Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
351
+ # Returns OneLogin::RubySaml::Settings pre-populated with IdP metadata
342
352
  settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
343
353
 
344
354
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
345
- settings.issuer = "http://#{request.host}/saml/metadata"
355
+ settings.sp_entity_id = "http://#{request.host}/saml/metadata"
346
356
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
347
357
  # Optional for most SAML IdPs
348
358
  settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
@@ -350,14 +360,15 @@ def saml_settings
350
360
  settings
351
361
  end
352
362
  ```
363
+
353
364
  The following attributes are set:
354
365
  * idp_entity_id
355
366
  * name_identifier_format
356
- * idp_sso_target_url
357
- * idp_slo_target_url
367
+ * idp_sso_service_url
368
+ * idp_slo_service_url
358
369
  * idp_attribute_names
359
- * idp_cert
360
- * idp_cert_fingerprint
370
+ * idp_cert
371
+ * idp_cert_fingerprint
361
372
  * idp_cert_multi
362
373
 
363
374
  ### Retrieve one Entity Descriptor when many exist in Metadata
@@ -368,11 +379,11 @@ IdpMetadataParser by its Entity Id value:
368
379
 
369
380
  ```ruby
370
381
  validate_cert = true
371
- settings = idp_metadata_parser.parse_remote(
372
- "https://example.com/auth/saml2/idp/metadata",
373
- validate_cert,
374
- entity_id: "http//example.com/target/entity"
375
- )
382
+ settings = idp_metadata_parser.parse_remote(
383
+ "https://example.com/auth/saml2/idp/metadata",
384
+ validate_cert,
385
+ entity_id: "http//example.com/target/entity"
386
+ )
376
387
  ```
377
388
 
378
389
  ### Parsing Metadata into an Hash
@@ -387,7 +398,7 @@ If you are using `saml:AttributeStatement` to transfer data like the username, y
387
398
  `single_value_compatibility` (when activated, only the first value is returned)
388
399
 
389
400
  ```ruby
390
- response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
401
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
391
402
  response.settings = saml_settings
392
403
 
393
404
  response.attributes[:username]
@@ -420,6 +431,9 @@ Imagine this `saml:AttributeStatement`
420
431
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
421
432
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
422
433
  </saml:Attribute>
434
+ <saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
435
+ <saml:AttributeValue>usersName</saml:AttributeValue>
436
+ </saml:Attribute>
423
437
  </saml:AttributeStatement>
424
438
  ```
425
439
 
@@ -430,7 +444,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
430
444
  "another_value"=>["value1", "value2"],
431
445
  "role"=>["role1", "role2", "role3"],
432
446
  "attribute_with_nil_value"=>[nil],
433
- "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}>
447
+ "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]
448
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}>
434
449
 
435
450
  # Active single_value_compatibility
436
451
  OneLogin::RubySaml::Attributes.single_value_compatibility = true
@@ -447,6 +462,9 @@ pp(response.attributes.single(:role))
447
462
  pp(response.attributes.multi(:role))
448
463
  # => ["role1", "role2", "role3"]
449
464
 
465
+ pp(response.attributes.fetch(:role))
466
+ # => "role1"
467
+
450
468
  pp(response.attributes[:attribute_with_nil_value])
451
469
  # => nil
452
470
 
@@ -462,7 +480,10 @@ pp(response.attributes.single(:not_exists))
462
480
  pp(response.attributes.multi(:not_exists))
463
481
  # => nil
464
482
 
465
- # Deactive single_value_compatibility
483
+ pp(response.attributes.fetch(/givenname/))
484
+ # => "usersName"
485
+
486
+ # Deprecated single_value_compatibility
466
487
  OneLogin::RubySaml::Attributes.single_value_compatibility = false
467
488
 
468
489
  pp(response.attributes[:uid])
@@ -477,6 +498,9 @@ pp(response.attributes.single(:role))
477
498
  pp(response.attributes.multi(:role))
478
499
  # => ["role1", "role2", "role3"]
479
500
 
501
+ pp(response.attributes.fetch(:role))
502
+ # => ["role1", "role2", "role3"]
503
+
480
504
  pp(response.attributes[:attribute_with_nil_value])
481
505
  # => [nil]
482
506
 
@@ -491,77 +515,181 @@ pp(response.attributes.single(:not_exists))
491
515
 
492
516
  pp(response.attributes.multi(:not_exists))
493
517
  # => nil
518
+
519
+ pp(response.attributes.fetch(/givenname/))
520
+ # => ["usersName"]
494
521
  ```
495
522
 
496
523
  The `saml:AuthnContextClassRef` of the AuthNRequest can be provided by `settings.authn_context`; possible values are described at [SAMLAuthnCxt]. The comparison method can be set using `settings.authn_context_comparison` parameter. Possible values include: 'exact', 'better', 'maximum' and 'minimum' (default value is 'exact').
497
524
  To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
498
525
 
526
+ In a SP-initiated flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
527
+ building the authrequest object.
499
528
 
500
- ## Signing
529
+ ## Service Provider Metadata
501
530
 
502
- The Ruby Toolkit supports 2 different kinds of signature: Embeded and `GET` parameters
531
+ To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
532
+ to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
503
533
 
504
- In order to be able to sign, define the private key and the public cert of the service provider:
534
+ The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
535
+
536
+ The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
537
+ to the IdP settings.
505
538
 
506
539
  ```ruby
507
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
508
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
540
+ class SamlController < ApplicationController
541
+ # ... the rest of your controller definitions ...
542
+ def metadata
543
+ settings = Account.get_saml_settings
544
+ meta = OneLogin::RubySaml::Metadata.new
545
+ render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
546
+ end
547
+ end
509
548
  ```
510
549
 
511
- The settings related to sign are stored in the `security` attribute of the settings:
550
+ You can add `ValidUntil` and `CacheDuration` to the SP Metadata XML using instead:
512
551
 
513
552
  ```ruby
514
- settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
515
- settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
516
- settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
517
- settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
518
- settings.security[:metadata_signed] = true # Enable or not signature on Metadata
553
+ # Valid until => 2 days from now
554
+ # Cache duration = 604800s = 1 week
555
+ valid_until = Time.now + 172800
556
+ cache_duration = 604800
557
+ meta.generate(settings, false, valid_until, cache_duration)
558
+ ```
559
+
560
+ ## Signing and Decryption
561
+
562
+ Ruby SAML supports the following functionality:
563
+
564
+ 1. Signing your SP Metadata XML
565
+ 2. Signing your SP SAML messages
566
+ 3. Decrypting IdP Assertion messages upon receipt (EncryptedAssertion)
567
+ 4. Verifying signatures on SAML messages and IdP Assertions
519
568
 
569
+ In order to use functions 1-3 above, you must first define your SP public certificate and private key:
570
+
571
+ ```ruby
572
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
573
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
574
+ ```
575
+
576
+ Note that the same certificate (and its associated private key) are used to perform
577
+ all decryption and signing-related functions (1-4) above. Ruby SAML does not currently allow
578
+ to specify different certificates for each function.
579
+
580
+ You may also globally set the SP signature and digest method, to be used in SP signing (functions 1 and 2 above):
581
+
582
+ ```ruby
520
583
  settings.security[:digest_method] = XMLSecurity::Document::SHA1
521
584
  settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
585
+ ```
586
+
587
+ #### Signing SP Metadata
588
+
589
+ You may add a `<ds:Signature>` digital signature element to your SP Metadata XML using the following setting:
590
+
591
+ ```ruby
592
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
593
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
594
+
595
+ settings.security[:metadata_signed] = true # Enable signature on Metadata
596
+ ```
597
+
598
+ #### Signing SP SAML Messages
599
+
600
+ Ruby SAML supports SAML request signing. The Service Provider will sign the
601
+ request/responses with its private key. The Identity Provider will then validate the signature
602
+ of the received request/responses with the public X.509 cert of the Service Provider.
603
+
604
+ To enable, please first set your certificate and private key. This will add `<md:KeyDescriptor use="signing">`
605
+ to your SP Metadata XML, to be read by the IdP.
522
606
 
523
- # Embeded signature or HTTP GET parameter signature
524
- # Note that metadata signature is always embedded regardless of this value.
525
- settings.security[:embed_sign] = false
607
+ ```ruby
608
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
609
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
610
+ ```
611
+
612
+ Next, you may specify the specific SP SAML messages you would like to sign:
613
+
614
+ ```ruby
615
+ settings.security[:authn_requests_signed] = true # Enable signature on AuthNRequest
616
+ settings.security[:logout_requests_signed] = true # Enable signature on Logout Request
617
+ settings.security[:logout_responses_signed] = true # Enable signature on Logout Response
526
618
  ```
527
619
 
528
- Notice that the RelayState parameter is used when creating the Signature on the HTTP-Redirect Binding.
620
+ Signatures will be handled automatically for both `HTTP-Redirect` and `HTTP-Redirect` Binding.
621
+ Note that the RelayState parameter is used when creating the Signature on the `HTTP-Redirect` Binding.
529
622
  Remember to provide it to the Signature builder if you are sending a `GET RelayState` parameter or the
530
623
  signature validation process will fail at the Identity Provider.
531
624
 
532
- The Service Provider will sign the request/responses with its private key.
533
- The Identity Provider will validate the sign of the received request/responses with the public x500 cert of the
534
- Service Provider.
625
+ #### Decrypting IdP SAML Assertions
535
626
 
536
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
627
+ Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
628
+ public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
537
629
 
538
- Enable/disable the soft mode with the `settings.soft` parameter. When set to `false`, saml validations errors will raise an exception.
630
+ You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encrytion">` to your
631
+ SP Metadata XML, to be read by the IdP.
539
632
 
540
- ## Decrypting
633
+ ```ruby
634
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
635
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
636
+
637
+ settings.security[:want_assertions_encrypted] = true # Invalidate SAML messages without an EncryptedAssertion
638
+ ```
541
639
 
542
- The Ruby Toolkit supports EncryptedAssertion.
640
+ #### Verifying Signature on IdP Assertions
641
+
642
+ You may require the IdP to sign its SAML Assertions using the following setting.
643
+ With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
644
+ The signature will be checked against the `<md:KeyDescriptor use="signing">` element
645
+ present in the IdP's metadata.
646
+
647
+ ```ruby
648
+ settings.security[:want_assertions_signed] = true # Require the IdP to sign its SAML Assertions
649
+ ```
650
+
651
+ #### Certificate and Signature Validation
652
+
653
+ You may require SP and IdP certificates to be non-expired using the following settings:
654
+
655
+ ```ruby
656
+ settings.security[:check_idp_cert_expiration] = true # Raise error if IdP X.509 cert is expired
657
+ settings.security[:check_sp_cert_expiration] = true # Raise error SP X.509 cert is expired
658
+ ```
543
659
 
544
- In order to be able to decrypt a SAML Response that contains a EncryptedAssertion you need define the private key and the public cert of the service provider, then share this with the Identity Provider.
660
+ By default, Ruby SAML will raise a `OneLogin::RubySaml::ValidationError` if a signature or certificate
661
+ validation fails. You may disable such exceptions using the `settings.security[:soft]` parameter.
545
662
 
546
663
  ```ruby
547
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
548
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
664
+ settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
549
665
  ```
550
666
 
551
- The Identity Provider will encrypt the Assertion with the public cert of the Service Provider.
552
- The Service Provider will decrypt the EncryptedAssertion with its private key.
667
+ #### Key Rollover
553
668
 
554
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
669
+ To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
670
+ `settings.certificate_new`. This will publish the new SP certificate in your metadata so that your IdP counterparties
671
+ may cache it in preparation for rollover.
555
672
 
673
+ For example, if you to rollover from `CERT A` to `CERT B`. Before rollover, your settings should look as follows.
674
+ Both `CERT A` and `CERT B` will now appear in your SP metadata, however `CERT A` will still be used for signing
675
+ and encryption at this time.
556
676
 
557
- ## Key rollover
677
+ ```ruby
678
+ settings.certificate = "CERT A"
679
+ settings.private_key = "PRIVATE KEY FOR CERT A"
680
+ settings.certificate_new = "CERT B"
681
+ ```
558
682
 
559
- If you plan to update the SP x509cert and privateKey you can define the parameter 'certificate_new' at the settings and that new SP public certificate will be published on the SP metadata so Identity Providers can read them and get ready for rollover.
683
+ After the IdP has cached `CERT B`, you may then change your settings as follows:
560
684
 
685
+ ```ruby
686
+ settings.certificate = "CERT B"
687
+ settings.private_key = "PRIVATE KEY FOR CERT B"
688
+ ```
561
689
 
562
690
  ## Single Log Out
563
691
 
564
- The Ruby Toolkit supports SP-initiated Single Logout and IdP-Initiated Single Logout.
692
+ Ruby SAML supports SP-initiated Single Logout and IdP-Initiated Single Logout.
565
693
 
566
694
  Here is an example that we could add to our previous controller to generate and send a SAML Logout Request to the IdP:
567
695
 
@@ -571,22 +699,28 @@ def sp_logout_request
571
699
  # LogoutRequest accepts plain browser requests w/o paramters
572
700
  settings = saml_settings
573
701
 
574
- if settings.idp_slo_target_url.nil?
702
+ if settings.idp_slo_service_url.nil?
575
703
  logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'"
576
704
  delete_session
577
705
  else
578
706
 
579
- # Since we created a new SAML request, save the transaction_id
580
- # to compare it with the response we get back
581
- logout_request = OneLogin::RubySaml::Logoutrequest.new()
582
- session[:transaction_id] = logout_request.uuid
583
- logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{session[:transaction_id]}'"
707
+ logout_request = OneLogin::RubySaml::Logoutrequest.new
708
+ logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
584
709
 
585
710
  if settings.name_identifier_value.nil?
586
711
  settings.name_identifier_value = session[:userid]
587
712
  end
588
713
 
589
- relayState = url_for controller: 'saml', action: 'index'
714
+ # Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
715
+ logged_user = session[:userid]
716
+ logger.info "Delete session for '#{session[:userid]}'"
717
+ delete_session
718
+
719
+ # Save the transaction_id to compare it with the response we get back
720
+ session[:transaction_id] = logout_request.uuid
721
+ session[:logged_out_user] = logged_user
722
+
723
+ relayState = url_for(controller: 'saml', action: 'index')
590
724
  redirect_to(logout_request.create(settings, :RelayState => relayState))
591
725
  end
592
726
  end
@@ -613,7 +747,7 @@ def process_logout_response
613
747
  logger.error "The SAML Logout Response is invalid"
614
748
  else
615
749
  # Actually log out this session
616
- logger.info "Delete session for '#{session[:userid]}'"
750
+ logger.info "SLO completed for '#{session[:logged_out_user]}'"
617
751
  delete_session
618
752
  end
619
753
  end
@@ -622,6 +756,8 @@ end
622
756
  def delete_session
623
757
  session[:userid] = nil
624
758
  session[:attributes] = nil
759
+ session[:transaction_id] = nil
760
+ session[:logged_out_user] = nil
625
761
  end
626
762
  ```
627
763
 
@@ -634,7 +770,7 @@ def idp_logout_request
634
770
  logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
635
771
  if !logout_request.is_valid?
636
772
  logger.error "IdP initiated LogoutRequest was not valid!"
637
- render :inline => logger.error
773
+ return render :inline => logger.error
638
774
  end
639
775
  logger.info "IdP initiated Logout for #{logout_request.name_id}"
640
776
 
@@ -666,30 +802,6 @@ def logout
666
802
  end
667
803
  ```
668
804
 
669
-
670
-
671
- ## Service Provider Metadata
672
-
673
- To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
674
- to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
675
-
676
- The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
677
-
678
- The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
679
- to the IdP settings.
680
-
681
- ```ruby
682
- class SamlController < ApplicationController
683
- # ... the rest of your controller definitions ...
684
- def metadata
685
- settings = Account.get_saml_settings
686
- meta = OneLogin::RubySaml::Metadata.new
687
- render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
688
- end
689
- end
690
- ```
691
-
692
-
693
805
  ## Clock Drift
694
806
 
695
807
  Server clocks tend to drift naturally. If during validation of the response you get the error "Current time is earlier than NotBefore condition", this may be due to clock differences between your system and that of the Identity Provider.
@@ -704,13 +816,33 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
704
816
 
705
817
  Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
706
818
 
819
+ ## Deflation Limit
820
+
821
+ To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
822
+ Sometimes legitimate SAML messages will exceed this limit,
823
+ for example due to custom claims like including groups a user is a member of.
824
+ If you want to customize this limit, you need to provide a different setting when initializing the response object.
825
+ Example:
826
+
827
+ ```ruby
828
+ def consume
829
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], { settings: saml_settings })
830
+ ...
831
+ end
832
+
833
+ private
834
+
835
+ def saml_settings
836
+ OneLogin::RubySaml::Settings.new(message_max_bytesize: 500_000)
837
+ end
838
+ ```
839
+
707
840
  ## Attribute Service
708
841
 
709
842
  To request attributes from the IdP the SP needs to provide an attribute service within it's metadata and reference the index in the assertion.
710
843
 
711
844
  ```ruby
712
845
  settings = OneLogin::RubySaml::Settings.new
713
-
714
846
  settings.attributes_index = 5
715
847
  settings.attribute_consuming_service.configure do
716
848
  service_name "Service"
@@ -721,3 +853,27 @@ end
721
853
  ```
722
854
 
723
855
  The `attribute_value` option additionally accepts an array of possible values.
856
+
857
+ ## Custom Metadata Fields
858
+
859
+ Some IdPs may require to add SPs to add additional fields (Organization, ContactPerson, etc.)
860
+ into the SP metadata. This can be achieved by extending the `OneLogin::RubySaml::Metadata`
861
+ class and overriding the `#add_extras` method as per the following example:
862
+
863
+ ```ruby
864
+ class MyMetadata < OneLogin::RubySaml::Metadata
865
+ def add_extras(root, _settings)
866
+ org = root.add_element("md:Organization")
867
+ org.add_element("md:OrganizationName", 'xml:lang' => "en-US").text = 'ACME Inc.'
868
+ org.add_element("md:OrganizationDisplayName", 'xml:lang' => "en-US").text = 'ACME'
869
+ org.add_element("md:OrganizationURL", 'xml:lang' => "en-US").text = 'https://www.acme.com'
870
+
871
+ cp = root.add_element("md:ContactPerson", 'contactType' => 'technical')
872
+ cp.add_element("md:GivenName").text = 'ACME SAML Team'
873
+ cp.add_element("md:EmailAddress").text = 'saml@acme.com'
874
+ end
875
+ end
876
+
877
+ # Output XML with custom metadata
878
+ MyMetadata.new.generate(settings)
879
+ ```