ruby-saml 1.12.4 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,169 +1,109 @@
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)](https://coveralls.io/r/onelogin/ruby-saml?branch=master)
1
+ # Ruby SAML
2
+ [![ruby-saml CI](https://github.com/SAML-Toolkits/ruby-saml/actions/workflows/test.yml/badge.svg)](https://github.com/SAML-Toolkits/ruby-saml/actions/workflows/test.yml)
3
+ [![Coverage Status](https://coveralls.io/repos/github/SAML-Toolkits/ruby-saml/badge.svg?branch=master)](https://coveralls.io/github/SAML-Toolkits/ruby-saml?branch=master)
4
+ [![Rubygem Version](https://badge.fury.io/rb/ruby-saml.svg)](https://badge.fury.io/rb/ruby-saml)
5
+ [![GitHub version](https://badge.fury.io/gh/SAML-Toolkits%2Fruby-saml.svg)](https://badge.fury.io/gh/SAML-Toolkits%2Fruby-saml) ![GitHub](https://img.shields.io/github/license/SAML-Toolkits/ruby-saml) ![Gem](https://img.shields.io/gem/dtv/ruby-saml?label=gem%20downloads%20latest) ![Gem](https://img.shields.io/gem/dt/ruby-saml?label=gem%20total%20downloads)
2
6
 
3
- ## Updating from 1.11.x to 1.12.0
4
- Version `1.12.0` adds support for gcm algorithm and
5
- change/adds specific error messages for signature validations
7
+ Minor and patch versions of Ruby SAML may introduce breaking changes. Please read
8
+ [UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
6
9
 
7
- `idp_sso_target_url` and `idp_slo_target_url` attributes of the Settings class deprecated in favor of `idp_sso_service_url` and `idp_slo_service_url`.
8
- In IDPMetadataParser, `parse`, `parse_to_hash` and `parse_to_array` methods now retrieve SSO URL and SLO URL endpoints with
9
- `idp_sso_service_url` and `idp_slo_service_url` (previously `idp_sso_target_url` and `idp_slo_target_url` respectively).
10
+ ### Pay it Forward: Support RubySAML and Strengthen Open-Source Security
10
11
 
11
- ## Updating from 1.10.x to 1.11.0
12
- Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`.
13
- There are two new security settings: `settings.security[:check_idp_cert_expiration]` and `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the IdP or SP X.509 certificate has expired, respectively.
12
+ RubySAML is a trusted authentication library used by startups and enterprises alike.
14
13
 
15
- Version `1.10.2` includes the `valid_until` attribute in parsed IdP metadata.
14
+ But security doesn't happen in a vacuum. Vulnerabilities in authentication libraries can
15
+ have widespread consequences. Maintaining open-source security requires continuous
16
+ effort, expertise, and funding. By supporting RubySAML, you’re not just securing your
17
+ own systems—you’re strengthening auth security globally.
16
18
 
17
- Version `1.10.1` improves Ruby 1.8.7 support.
19
+ #### How you can help
18
20
 
19
- ## Updating from 1.9.0 to 1.10.0
20
- Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor, Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user to be authenticated and updates the format_cert method to accept certs with /\x0d/
21
+ * Sponsor RubySAML: [GitHub Sponsors](https://github.com/sponsors/SAML-Toolkits)
22
+ * Contribute to secure-by-design improvements
23
+ * Responsibly report vulnerabilities (see "Vulnerability Reporting" above)
21
24
 
22
- ## Updating from 1.8.0 to 1.9.0
23
- Version `1.9.0` better supports Ruby 2.4+ and JRuby 9.2.0.0. `Settings` initialization now has a second parameter, `keep_security_settings` (default: false), which saves security settings attributes that are not explicitly overridden, if set to true.
25
+ Security is a shared responsibility. If RubySAML has helped your organization, please
26
+ consider giving back. Together, we can keep authentication secure.
24
27
 
25
- ## Updating from 1.7.X to 1.8.0
26
- 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.
28
+ ### Sponsors
27
29
 
28
- ## Updating from 1.6.0 to 1.7.0
30
+ Thanks to the following sponsors for securing the open source ecosystem,
29
31
 
30
- 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.
32
+ [<img alt="84codes" src="https://avatars.githubusercontent.com/u/5353257" width="75px">](https://www.84codes.com)
31
33
 
32
- ## Updating from 1.5.0 to 1.6.0
33
34
 
34
- 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.
35
+ ## Vulnerabilities
35
36
 
36
- The new preferred way to provide _SAMLResponse_, _RelayState_, and _SigAlg_ is via the `options[:raw_get_params]` parameter. For example:
37
+ CVE-2025-54572 affects version ruby-saml < 1.18.1
37
38
 
38
- ```ruby
39
- # In this example `query_params` is assumed to contain decoded query parameters,
40
- # and `raw_query_params` is assumed to contain encoded query parameters as sent by the IDP.
41
- settings = {
42
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
43
- settings.soft = false
44
- }
45
- options = {
46
- get_params: {
47
- "Signature" => query_params["Signature"],
48
- },
49
- raw_get_params: {
50
- "SAMLRequest" => raw_query_params["SAMLRequest"],
51
- "SigAlg" => raw_query_params["SigAlg"],
52
- "RelayState" => raw_query_params["RelayState"],
53
- },
54
- }
55
- slo_logout_request = OneLogin::RubySaml::SloLogoutrequest.new(query_params["SAMLRequest"], settings, options)
56
- raise "Invalid Logout Request" unless slo_logout_request.is_valid?
57
- ```
58
-
59
- 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.
60
-
61
- ## Updating from 1.4.2 to 1.4.3
62
-
63
- Version `1.4.3` introduces Recipient validation of SubjectConfirmation elements.
64
- The 'Recipient' value is compared with the settings.assertion_consumer_service_url
65
- value.
66
- If you want to skip that validation, add the :skip_recipient_check option to the
67
- initialize method of the Response object.
68
-
69
- Parsing metadata that contains more than one certificate will propagate the
70
- idp_cert_multi property rather than idp_cert. See [signature validation
71
- section](#signature-validation) for details.
72
-
73
- ## Updating from 1.3.x to 1.4.X
74
-
75
- Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
76
39
 
77
- ## Updating from 1.2.x to 1.3.X
40
+ There are critical vulnerabilities affecting ruby-saml < 1.18.0, two of them allows SAML authentication bypass (CVE-2025-25291, CVE-2025-25292, CVE-2025-25293). Please upgrade to a fixed version (1.18.0)
78
41
 
79
- 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)
80
-
81
- ## Updating from 1.1.x to 1.2.X
82
-
83
- Version `1.2` adds IDP metadata parsing improvements, uuid deprecation in favour of SecureRandom, refactor error handling and some minor improvements
84
-
85
- There is no compatibility issue detected.
86
-
87
- For more details, please review [the changelog](changelog.md).
88
-
89
- ## Updating from 1.0.x to 1.1.X
90
-
91
- Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
92
-
93
- ## Updating from 0.9.x to 1.0.X
94
-
95
- Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
42
+ ## Overview
96
43
 
97
- Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
44
+ The Ruby SAML library is for implementing the client side of a SAML authorization,
45
+ i.e. it provides a means for managing authorization initialization and confirmation
46
+ requests from identity providers.
98
47
 
99
- ### Important Changes
100
- 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.
48
+ SAML authorization is a two-step process and you are expected to implement support for both.
101
49
 
102
- ## Updating from 0.8.x to 0.9.x
103
- Version `0.9` adds many new features and improvements.
50
+ We created a demo project for Rails 4 that uses the latest version of this library:
51
+ [ruby-saml-example](https://github.com/saml-toolkits/ruby-saml-example)
104
52
 
105
- ## Updating from 0.7.x to 0.8.x
106
- Version `0.8.x` changes the namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
53
+ ### Supported Ruby Versions
107
54
 
108
- ## Overview
55
+ The following Ruby versions are covered by CI testing:
109
56
 
110
- 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.
111
-
112
- SAML authorization is a two step process and you are expected to implement support for both.
113
-
114
- 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)
115
-
116
- ### Supported versions of Ruby
117
- * 1.8.7
118
- * 1.9.x
119
- * 2.0.x
120
- * 2.1.x
121
- * 2.2.x
122
- * 2.3.x
123
- * 2.4.x
124
- * 2.5.x
125
- * 2.6.x
126
- * 2.7.x
127
- * 3.0.x
128
- * JRuby 1.7.x
129
- * JRuby 9.0.x
130
- * JRuby 9.1.x
131
- * JRuby 9.2.x
57
+ * Ruby (MRI) 2.1 to 3.4
58
+ * JRuby 9.1 to 9.4
59
+ * TruffleRuby (latest)
132
60
 
133
61
  ## Adding Features, Pull Requests
62
+
134
63
  * Fork the repository
135
64
  * Make your feature addition or bug fix
136
65
  * Add tests for your new features. This is important so we don't break any features in a future version unintentionally.
137
- * Ensure all tests pass.
138
- * Do not change rakefile, version, or history.
66
+ * Ensure all tests pass by running `bundle exec rake test`.
67
+ * Do not change Rakefile, version, or history.
139
68
  * Open a pull request, following [this template](https://gist.github.com/Lordnibbler/11002759).
140
69
 
141
70
  ## Security Guidelines
142
71
 
143
- 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.
72
+ If you believe you have discovered a security vulnerability in this gem, please report it
73
+ by mail to the maintainer: sixto.martin.garcia+security@gmail.com
144
74
 
145
- ### Security warning
75
+ ### Security Warning
146
76
 
147
77
  Some tools may incorrectly report ruby-saml is a potential security vulnerability.
148
- ruby-saml depends on Nokogiri, and it's possible to use Nokogiri in a dangerous way
78
+ ruby-saml depends on Nokogiri, and it is possible to use Nokogiri in a dangerous way
149
79
  (by enabling its DTDLOAD option and disabling its NONET option).
150
80
  This dangerous Nokogiri configuration, which is sometimes used by other components,
151
81
  can create an XML External Entity (XXE) vulnerability if the XML data is not trusted.
152
82
  However, ruby-saml never enables this dangerous Nokogiri configuration;
153
83
  ruby-saml never enables DTDLOAD, and it never disables NONET.
154
84
 
85
+ The OneLogin::RubySaml::IdpMetadataParser class does not validate the provided URL before parsing.
86
+
87
+ Usually, the same administrator who handles the Service Provider also sets the URL to
88
+ the IdP, which should be a trusted resource.
89
+
90
+ But there are other scenarios, like a SaaS app where the administrator of the app
91
+ delegates this functionality to other users. In this case, extra precautions should
92
+ be taken in order to validate such URL inputs and avoid attacks like SSRF.
155
93
 
156
94
  ## Getting Started
157
- 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:
95
+
96
+ In order to use Ruby SAML you will need to install the gem (either manually or using Bundler),
97
+ and require the library in your Ruby application:
158
98
 
159
99
  Using `Gemfile`
160
100
 
161
101
  ```ruby
162
102
  # latest stable
163
- gem 'ruby-saml', '~> 1.11.0'
103
+ gem 'ruby-saml', '~> 1.18.0'
164
104
 
165
105
  # or track master for bleeding-edge
166
- gem 'ruby-saml', :github => 'onelogin/ruby-saml'
106
+ gem 'ruby-saml', :github => 'saml-toolkit/ruby-saml'
167
107
  ```
168
108
 
169
109
  Using RubyGems
@@ -172,7 +112,8 @@ Using RubyGems
172
112
  gem install ruby-saml
173
113
  ```
174
114
 
175
- When requiring the gem, you can add the whole toolkit
115
+ You may require the entire Ruby SAML gem:
116
+
176
117
  ```ruby
177
118
  require 'onelogin/ruby-saml'
178
119
  ```
@@ -185,7 +126,9 @@ require 'onelogin/ruby-saml/authrequest'
185
126
 
186
127
  ### Installation on Ruby 1.8.7
187
128
 
188
- 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.
129
+ This gem uses Nokogiri as a dependency, which dropped support for Ruby 1.8.x in Nokogiri 1.6.
130
+ When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri
131
+ prior to 1.6 is installed or specified if it hasn't been already.
189
132
 
190
133
  Using `Gemfile`
191
134
 
@@ -202,8 +145,8 @@ gem install nokogiri --version '~> 1.5.10'
202
145
  ### Configuring Logging
203
146
 
204
147
  When troubleshooting SAML integration issues, you will find it extremely helpful to examine the
205
- output of this gem's business logic. By default, log messages are emitted to RAILS_DEFAULT_LOGGER
206
- when the gem is used in a Rails context, and to STDOUT when the gem is used outside of Rails.
148
+ output of this gem's business logic. By default, log messages are emitted to `RAILS_DEFAULT_LOGGER`
149
+ when the gem is used in a Rails context, and to `STDOUT` when the gem is used outside of Rails.
207
150
 
208
151
  To override the default behavior and control the destination of log messages, provide
209
152
  a ruby Logger object to the gem's logging singleton:
@@ -214,7 +157,10 @@ OneLogin::RubySaml::Logging.logger = Logger.new('/var/log/ruby-saml.log')
214
157
 
215
158
  ## The Initialization Phase
216
159
 
217
- 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):
160
+ This is the first request you will get from the identity provider. It will hit your application
161
+ at a specific URL that you've announced as your SAML initialization point. The response to
162
+ this initialization is a redirect back to the identity provider, which can look something
163
+ like this (ignore the saml_settings method call for now):
218
164
 
219
165
  ```ruby
220
166
  def init
@@ -223,7 +169,7 @@ def init
223
169
  end
224
170
  ```
225
171
 
226
- If the SP knows who should be authenticated in the IdP, then can provide that info as follows:
172
+ If the SP knows who should be authenticated in the IdP, it can provide that info as follows:
227
173
 
228
174
  ```ruby
229
175
  def init
@@ -234,7 +180,10 @@ def init
234
180
  end
235
181
  ```
236
182
 
237
- 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):
183
+ Once you've redirected back to the identity provider, it will ensure that the user has been
184
+ authorized and redirect back to your application for final consumption.
185
+ This can look something like this (the `authorize_success` and `authorize_failure`
186
+ methods are specific to your application):
238
187
 
239
188
  ```ruby
240
189
  def consume
@@ -252,16 +201,19 @@ def consume
252
201
  end
253
202
  ```
254
203
 
255
- 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:
204
+ In the above there are a few assumptions, one being that `response.nameid` is an email address.
205
+ This is all handled with how you specify the settings that are in play via the `saml_settings` method.
206
+ That could be implemented along the lines of this:
256
207
 
257
208
  ```
258
209
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
259
210
  response.settings = saml_settings
260
211
  ```
261
212
 
262
- If the assertion of the SAMLResponse is not encrypted, you can initialize the Response without the `:settings` parameter and set it later.
263
- If the SAMLResponse contains an encrypted assertion, you need to provide the settings in the
264
- initialize method in order to obtain the decrypted assertion, using the service provider private key in order to decrypt.
213
+ If the assertion of the SAMLResponse is not encrypted, you can initialize the Response
214
+ without the `:settings` parameter and set it later. If the SAMLResponse contains an encrypted
215
+ assertion, you need to provide the settings in the initialize method in order to obtain the
216
+ decrypted assertion, using the service provider private key in order to decrypt.
265
217
  If you don't know what expect, always use the former (set the settings on initialize).
266
218
 
267
219
  ```ruby
@@ -271,8 +223,10 @@ def saml_settings
271
223
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
272
224
  settings.sp_entity_id = "http://#{request.host}/saml/metadata"
273
225
  settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}"
274
- settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
275
- settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
226
+ settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
227
+ settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
228
+ settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
229
+ settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
276
230
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
277
231
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
278
232
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@@ -285,27 +239,30 @@ def saml_settings
285
239
  "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
286
240
  ]
287
241
 
288
- # Optional bindings (defaults to Redirect for logout POST for acs)
289
- settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
290
- settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
242
+ # Optional bindings (defaults to Redirect for logout POST for ACS)
243
+ settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
244
+ settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
291
245
 
292
246
  settings
293
247
  end
294
248
  ```
295
249
 
296
- The use of settings.issuer is deprecated in favour of settings.sp_entity_id since version 1.11.0
250
+ The use of `settings.issuer` is deprecated in favor of `settings.sp_entity_id` since version 1.11.0
297
251
 
298
- 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:
252
+ Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`.
253
+ For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation`
254
+ validations by initializing the response with different options:
299
255
 
300
256
  ```ruby
301
257
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
302
258
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
303
259
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
304
- 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
260
+ 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
305
261
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
306
262
  ```
307
263
 
308
- 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:
264
+ All that's left is to wrap everything in a controller and reference it in the initialization and
265
+ consumption URLs in OneLogin. A full controller example could look like this:
309
266
 
310
267
  ```ruby
311
268
  # This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application.
@@ -358,44 +315,56 @@ class SamlController < ApplicationController
358
315
  end
359
316
  ```
360
317
 
318
+ ## Signature Validation
361
319
 
362
- ## Signature validation
320
+ Ruby SAML allows different ways to validate the signature of the SAMLResponse:
321
+ - You can provide the IdP X.509 public certificate at the `idp_cert` setting.
322
+ - You can provide the IdP X.509 public certificate in fingerprint format using the
323
+ `idp_cert_fingerprint` setting parameter and additionally the `idp_cert_fingerprint_algorithm` parameter.
363
324
 
364
- On the ruby-saml toolkit there are different ways to validate the signature of the SAMLResponse:
365
- - You can provide the IdP x509 public certificate at the 'idp_cert' setting.
366
- - 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.
325
+ When validating the signature of redirect binding, the fingerprint is useless and the certificate
326
+ of the IdP is required in order to execute the validation. You can pass the option
327
+ `:relax_signature_validation` to `SloLogoutrequest` and `Logoutresponse` if want to avoid signature
328
+ validation if no certificate of the IdP is provided.
367
329
 
368
- 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.
369
- 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.
330
+ In production also we highly recommend to register on the settings the IdP certificate instead
331
+ of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision
332
+ attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism,
333
+ we maintain it for compatibility and also to be used on test environment.
370
334
 
371
- In production also we highly recommend to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment.
335
+ ## Handling Multiple IdP Certificates
372
336
 
373
- 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.
337
+ If the IdP metadata XML includes multiple certificates, you may specify the `idp_cert_multi`
338
+ parameter. When used, the `idp_cert` and `idp_cert_fingerprint` parameters are ignored.
339
+ This is useful in the following scenarios:
374
340
 
375
- In order to handle that the toolkit offers the 'idp_cert_multi' parameter.
376
- When used, 'idp_cert' and 'idp_cert_fingerprint' values are ignored.
341
+ * The IdP uses different certificates for signing versus encryption.
342
+ * The IdP is undergoing a key rollover and is publishing the old and new certificates in parallel.
377
343
 
378
- That 'idp_cert_multi' must be a Hash as follows:
344
+ The `idp_cert_multi` must be a `Hash` as follows. The `:signing` and `:encryption` arrays below,
345
+ add the IdP X.509 public certificates which were published in the IdP metadata.
346
+
347
+ ```ruby
379
348
  {
380
349
  :signing => [],
381
350
  :encryption => []
382
351
  }
383
-
384
- And on 'signing' and 'encryption' arrays, add the different IdP x509 public certificates published on the IdP metadata.
385
-
352
+ ```
386
353
 
387
354
  ## Metadata Based Configuration
388
355
 
389
- 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
390
- key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata.
356
+ The method above requires a little extra work to manually specify attributes about both the IdP and your SP application.
357
+ There's an easier method: use a metadata exchange. Metadata is an XML file that defines the capabilities of both the IdP
358
+ and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship.
359
+ The IdP administrator can also configure custom settings for an SP based on the metadata.
391
360
 
392
- Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings without further ado.
361
+ Using `IdpMetadataParser#parse_remote`, the IdP metadata will be added to the settings.
393
362
 
394
363
  ```ruby
395
364
  def saml_settings
396
365
 
397
366
  idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
398
- # Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
367
+ # Returns OneLogin::RubySaml::Settings pre-populated with IdP metadata
399
368
  settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
400
369
 
401
370
  settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
@@ -407,6 +376,7 @@ def saml_settings
407
376
  settings
408
377
  end
409
378
  ```
379
+
410
380
  The following attributes are set:
411
381
  * idp_entity_id
412
382
  * name_identifier_format
@@ -425,11 +395,32 @@ IdpMetadataParser by its Entity Id value:
425
395
 
426
396
  ```ruby
427
397
  validate_cert = true
428
- settings = idp_metadata_parser.parse_remote(
429
- "https://example.com/auth/saml2/idp/metadata",
430
- validate_cert,
431
- entity_id: "http//example.com/target/entity"
432
- )
398
+ settings = idp_metadata_parser.parse_remote(
399
+ "https://example.com/auth/saml2/idp/metadata",
400
+ validate_cert,
401
+ entity_id: "http//example.com/target/entity"
402
+ )
403
+ ```
404
+
405
+ ### Retrieve one Entity Descriptor with a specific binding and nameid format when several are available
406
+
407
+ If the metadata contains multiple bindings and NameID formats, the relevant ones
408
+ can be specified when retrieving the settings from the IdpMetadataParser
409
+ by the values of binding and NameID:
410
+
411
+ ```ruby
412
+ validate_cert = true
413
+ options = {
414
+ entity_id: "http//example.com/target/entity",
415
+ name_id_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
416
+ sso_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
417
+ slo_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
418
+ }
419
+ settings = idp_metadata_parser.parse_remote(
420
+ "https://example.com/auth/saml2/idp/metadata",
421
+ validate_cert,
422
+ options
423
+ )
433
424
  ```
434
425
 
435
426
  ### Parsing Metadata into an Hash
@@ -438,13 +429,58 @@ The `OneLogin::RubySaml::IdpMetadataParser` also provides the methods `#parse_to
438
429
  Those return an Hash instead of a `Settings` object, which may be useful for configuring
439
430
  [omniauth-saml](https://github.com/omniauth/omniauth-saml), for instance.
440
431
 
432
+
433
+ ### Validating Signature of Metadata and retrieve settings
434
+
435
+ Right now there is no method at ruby_saml to validate the signature of the metadata that gonna be parsed,
436
+ but it can be done as follows:
437
+ * Download the XML.
438
+ * Validate the Signature, providing the cert.
439
+ * Provide the XML to the parse method if the signature was validated
440
+
441
+ ```ruby
442
+ require "xml_security"
443
+ require "onelogin/ruby-saml/utils"
444
+ require "onelogin/ruby-saml/idp_metadata_parser"
445
+
446
+ url = "<url_to_the_metadata>"
447
+ idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
448
+
449
+ uri = URI.parse(url)
450
+ raise ArgumentError.new("url must begin with http or https") unless /^https?/ =~ uri.scheme
451
+ http = Net::HTTP.new(uri.host, uri.port)
452
+ if uri.scheme == "https"
453
+ http.use_ssl = true
454
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
455
+ end
456
+
457
+ get = Net::HTTP::Get.new(uri.request_uri)
458
+ get.basic_auth uri.user, uri.password if uri.user
459
+ response = http.request(get)
460
+ xml = response.body
461
+ errors = []
462
+ doc = XMLSecurity::SignedDocument.new(xml, errors)
463
+ cert_str = "<include_cert_here>"
464
+ cert = OneLogin::RubySaml::Utils.format_cert("cert_str")
465
+ metadata_sign_cert = OpenSSL::X509::Certificate.new(cert)
466
+ valid = doc.validate_document_with_cert(metadata_sign_cert, true)
467
+ if valid
468
+ settings = idp_metadata_parser.parse(
469
+ xml,
470
+ entity_id: "<entity_id_of_the_entity_to_be_retrieved>"
471
+ )
472
+ else
473
+ print "Metadata Signature failed to be verified with the cert provided"
474
+ end
475
+ ```
476
+
441
477
  ## Retrieving Attributes
442
478
 
443
- If you are using `saml:AttributeStatement` to transfer data like the username, you can access all the attributes through `response.attributes`. It contains all the `saml:AttributeStatement`s with its 'Name' as an indifferent key and one or more `saml:AttributeValue`s as values. The value returned depends on the value of the
479
+ If you are using `saml:AttributeStatement` to transfer data, such as the username, you can access all the attributes through `response.attributes`. It contains all the `saml:AttributeStatement`s with its 'Name' as an indifferent key and one or more `saml:AttributeValue`s as values. The value returned depends on the value of the
444
480
  `single_value_compatibility` (when activated, only the first value is returned)
445
481
 
446
482
  ```ruby
447
- response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
483
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
448
484
  response.settings = saml_settings
449
485
 
450
486
  response.attributes[:username]
@@ -529,7 +565,7 @@ pp(response.attributes.multi(:not_exists))
529
565
  pp(response.attributes.fetch(/givenname/))
530
566
  # => "usersName"
531
567
 
532
- # Deactive single_value_compatibility
568
+ # Deprecated single_value_compatibility
533
569
  OneLogin::RubySaml::Attributes.single_value_compatibility = false
534
570
 
535
571
  pp(response.attributes[:uid])
@@ -572,74 +608,212 @@ To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
572
608
  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
573
609
  building the authrequest object.
574
610
 
611
+ ## Service Provider Metadata
575
612
 
576
- ## Signing
613
+ To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
614
+ to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
577
615
 
578
- The Ruby Toolkit supports 2 different kinds of signature: Embeded and `GET` parameters
616
+ 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.
579
617
 
580
- In order to be able to sign, define the private key and the public cert of the service provider:
618
+ The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
619
+ to the IdP settings.
581
620
 
582
621
  ```ruby
583
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
584
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
622
+ class SamlController < ApplicationController
623
+ # ... the rest of your controller definitions ...
624
+ def metadata
625
+ settings = Account.get_saml_settings
626
+ meta = OneLogin::RubySaml::Metadata.new
627
+ render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
628
+ end
629
+ end
585
630
  ```
586
631
 
587
- The settings related to sign are stored in the `security` attribute of the settings:
632
+ You can add `ValidUntil` and `CacheDuration` to the SP Metadata XML using instead:
588
633
 
589
634
  ```ruby
590
- settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
591
- settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
592
- settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
593
- settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
594
- settings.security[:metadata_signed] = true # Enable or not signature on Metadata
635
+ # Valid until => 2 days from now
636
+ # Cache duration = 604800s = 1 week
637
+ valid_until = Time.now + 172800
638
+ cache_duration = 604800
639
+ meta.generate(settings, false, valid_until, cache_duration)
640
+ ```
641
+
642
+ ## Signing and Decryption
643
+
644
+ Ruby SAML supports the following functionality:
645
+
646
+ 1. Signing your SP Metadata XML
647
+ 2. Signing your SP SAML messages
648
+ 3. Decrypting IdP Assertion messages upon receipt (EncryptedAssertion)
649
+ 4. Verifying signatures on SAML messages and IdP Assertions
650
+
651
+ In order to use functions 1-3 above, you must first define your SP public certificate and private key:
652
+
653
+ ```ruby
654
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
655
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
656
+ ```
657
+
658
+ Note that the same certificate (and its associated private key) are used to perform
659
+ all decryption and signing-related functions (1-4) above. Ruby SAML does not currently allow
660
+ to specify different certificates for each function.
595
661
 
662
+ You may also globally set the SP signature and digest method, to be used in SP signing (functions 1 and 2 above):
663
+
664
+ ```ruby
596
665
  settings.security[:digest_method] = XMLSecurity::Document::SHA1
597
666
  settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
667
+ ```
668
+
669
+ #### Signing SP Metadata
670
+
671
+ You may add a `<ds:Signature>` digital signature element to your SP Metadata XML using the following setting:
672
+
673
+ ```ruby
674
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
675
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
598
676
 
599
- # Embeded signature or HTTP GET parameter signature
600
- # Note that metadata signature is always embedded regardless of this value.
601
- settings.security[:embed_sign] = false
602
- settings.security[:check_idp_cert_expiration] = false # Enable or not IdP x509 cert expiration check
603
- settings.security[:check_sp_cert_expiration] = false # Enable or not SP x509 cert expiration check
677
+ settings.security[:metadata_signed] = true # Enable signature on Metadata
604
678
  ```
605
679
 
606
- Notice that the RelayState parameter is used when creating the Signature on the HTTP-Redirect Binding.
680
+ #### Signing SP SAML Messages
681
+
682
+ Ruby SAML supports SAML request signing. The Service Provider will sign the
683
+ request/responses with its private key. The Identity Provider will then validate the signature
684
+ of the received request/responses with the public X.509 cert of the Service Provider.
685
+
686
+ To enable, please first set your certificate and private key. This will add `<md:KeyDescriptor use="signing">`
687
+ to your SP Metadata XML, to be read by the IdP.
688
+
689
+ ```ruby
690
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
691
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
692
+ ```
693
+
694
+ Next, you may specify the specific SP SAML messages you would like to sign:
695
+
696
+ ```ruby
697
+ settings.security[:authn_requests_signed] = true # Enable signature on AuthNRequest
698
+ settings.security[:logout_requests_signed] = true # Enable signature on Logout Request
699
+ settings.security[:logout_responses_signed] = true # Enable signature on Logout Response
700
+ ```
701
+
702
+ Signatures will be handled automatically for both `HTTP-Redirect` and `HTTP-POST` Binding.
703
+ Note that the RelayState parameter is used when creating the Signature on the `HTTP-Redirect` Binding.
607
704
  Remember to provide it to the Signature builder if you are sending a `GET RelayState` parameter or the
608
705
  signature validation process will fail at the Identity Provider.
609
706
 
610
- The Service Provider will sign the request/responses with its private key.
611
- The Identity Provider will validate the sign of the received request/responses with the public x509 cert of the
612
- Service Provider.
707
+ #### Decrypting IdP SAML Assertions
613
708
 
614
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
709
+ Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
710
+ public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
615
711
 
616
- Enable/disable the soft mode with the `settings.soft` parameter. When set to `false`, saml validations errors will raise an exception.
712
+ You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encryption">` to your
713
+ SP Metadata XML, to be read by the IdP.
617
714
 
618
- ## Decrypting
715
+ ```ruby
716
+ settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
717
+ settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
619
718
 
620
- The Ruby Toolkit supports EncryptedAssertion.
719
+ settings.security[:want_assertions_encrypted] = true # Invalidate SAML messages without an EncryptedAssertion
720
+ ```
721
+
722
+ #### Verifying Signature on IdP Assertions
621
723
 
622
- 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.
724
+ You may require the IdP to sign its SAML Assertions using the following setting.
725
+ With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
726
+ The signature will be checked against the `<md:KeyDescriptor use="signing">` element
727
+ present in the IdP's metadata.
623
728
 
624
729
  ```ruby
625
- settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
626
- settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
730
+ settings.security[:want_assertions_signed] = true # Require the IdP to sign its SAML Assertions
627
731
  ```
628
732
 
629
- The Identity Provider will encrypt the Assertion with the public cert of the Service Provider.
630
- The Service Provider will decrypt the EncryptedAssertion with its private key.
733
+ #### Certificate and Signature Validation
734
+
735
+ You may require SP and IdP certificates to be non-expired using the following settings:
736
+
737
+ ```ruby
738
+ settings.security[:check_idp_cert_expiration] = true # Raise error if IdP X.509 cert is expired
739
+ settings.security[:check_sp_cert_expiration] = true # Raise error SP X.509 cert is expired
740
+ ```
631
741
 
632
- Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
742
+ By default, Ruby SAML will raise a `OneLogin::RubySaml::ValidationError` if a signature or certificate
743
+ validation fails. You may disable such exceptions using the `settings.security[:soft]` parameter.
633
744
 
745
+ ```ruby
746
+ settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
747
+ ```
634
748
 
635
- ## Key rollover
749
+ #### Advanced SP Certificate Usage & Key Rollover
636
750
 
637
- 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.
751
+ Ruby SAML provides the `settings.sp_cert_multi` parameter to enable the following
752
+ advanced usage scenarios:
753
+ - Rotating SP certificates and private keys without disruption of service.
754
+ - Specifying separate SP certificates for signing and encryption.
638
755
 
756
+ The `sp_cert_multi` parameter replaces `certificate` and `private_key`
757
+ (you may not specify both pparameters at the same time.) `sp_cert_multi` has the following shape:
758
+
759
+ ```ruby
760
+ settings.sp_cert_multi = {
761
+ signing: [
762
+ { certificate: cert1, private_key: private_key1 },
763
+ { certificate: cert2, private_key: private_key2 }
764
+ ],
765
+ encryption: [
766
+ { certificate: cert1, private_key: private_key1 },
767
+ { certificate: cert3, private_key: private_key1 }
768
+ ],
769
+ }
770
+ ```
771
+
772
+ Certificate rotation is acheived by inserting new certificates at the bottom of each list,
773
+ and then removing the old certificates from the top of the list once your IdPs have migrated.
774
+ A common practice is for apps to publish the current SP metadata at a URL endpoint and have
775
+ the IdP regularly poll for updates.
776
+
777
+ Note the following:
778
+ - You may re-use the same certificate and/or private key in multiple places, including for both signing and encryption.
779
+ - The IdP should attempt to verify signatures with *all* `:signing` certificates,
780
+ and permit if *any one* succeeds. When signing, Ruby SAML will use the first SP certificate
781
+ in the `sp_cert_multi[:signing]` array. This will be the first active/non-expired certificate
782
+ in the array if `settings.security[:check_sp_cert_expiration]` is true.
783
+ - The IdP may encrypt with any of the SP certificates in the `sp_cert_multi[:encryption]`
784
+ array. When decrypting, Ruby SAML attempt to decrypt with each SP private key in
785
+ `sp_cert_multi[:encryption]` until the decryption is successful. This will skip private
786
+ keys for inactive/expired certificates if `:check_sp_cert_expiration` is true.
787
+ - If `:check_sp_cert_expiration` is true, the generated SP metadata XML will not include
788
+ inactive/expired certificates. This avoids validation errors when the IdP reads the SP
789
+ metadata.
790
+
791
+ #### Audience Validation
792
+
793
+ A service provider should only consider a SAML response valid if the IdP includes an <AudienceRestriction>
794
+ element containing an <Audience> element that uniquely identifies the service provider. Unless you specify
795
+ the `skip_audience` option, Ruby SAML will validate that each SAML response includes an <Audience> element
796
+ whose contents matches `settings.sp_entity_id`.
797
+
798
+ By default, Ruby SAML considers an <AudienceRestriction> element containing only empty <Audience> elements
799
+ to be valid. That means an otherwise valid SAML response with a condition like this would be valid:
800
+
801
+ ```xml
802
+ <AudienceRestriction>
803
+ <Audience />
804
+ </AudienceRestriction>
805
+ ```
806
+
807
+ You may enforce that an <AudienceRestriction> element containing only empty <Audience> elements
808
+ is invalid using the `settings.security[:strict_audience_validation]` parameter.
809
+
810
+ ```ruby
811
+ settings.security[:strict_audience_validation] = true
812
+ ```
639
813
 
640
814
  ## Single Log Out
641
815
 
642
- The Ruby Toolkit supports SP-initiated Single Logout and IdP-Initiated Single Logout.
816
+ Ruby SAML supports SP-initiated Single Logout and IdP-Initiated Single Logout.
643
817
 
644
818
  Here is an example that we could add to our previous controller to generate and send a SAML Logout Request to the IdP:
645
819
 
@@ -654,7 +828,7 @@ def sp_logout_request
654
828
  delete_session
655
829
  else
656
830
 
657
- logout_request = OneLogin::RubySaml::Logoutrequest.new()
831
+ logout_request = OneLogin::RubySaml::Logoutrequest.new
658
832
  logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'"
659
833
 
660
834
  if settings.name_identifier_value.nil?
@@ -670,7 +844,7 @@ def sp_logout_request
670
844
  session[:transaction_id] = logout_request.uuid
671
845
  session[:logged_out_user] = logged_user
672
846
 
673
- relayState = url_for controller: 'saml', action: 'index'
847
+ relayState = url_for(controller: 'saml', action: 'index')
674
848
  redirect_to(logout_request.create(settings, :RelayState => relayState))
675
849
  end
676
850
  end
@@ -717,7 +891,13 @@ Here is an example that we could add to our previous controller to process a SAM
717
891
  # Method to handle IdP initiated logouts
718
892
  def idp_logout_request
719
893
  settings = Account.get_saml_settings
720
- logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest])
894
+ # ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
895
+ # uppercase. Turn it True for ADFS compatibility on signature verification
896
+ settings.security[:lowercase_url_encoding] = true
897
+
898
+ logout_request = OneLogin::RubySaml::SloLogoutrequest.new(
899
+ params[:SAMLRequest], settings: settings
900
+ )
721
901
  if !logout_request.is_valid?
722
902
  logger.error "IdP initiated LogoutRequest was not valid!"
723
903
  return render :inline => logger.error
@@ -752,38 +932,6 @@ def logout
752
932
  end
753
933
  ```
754
934
 
755
-
756
-
757
- ## Service Provider Metadata
758
-
759
- To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
760
- to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
761
-
762
- 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.
763
-
764
- The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
765
- to the IdP settings.
766
-
767
- ```ruby
768
- class SamlController < ApplicationController
769
- # ... the rest of your controller definitions ...
770
- def metadata
771
- settings = Account.get_saml_settings
772
- meta = OneLogin::RubySaml::Metadata.new
773
- render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
774
- end
775
- end
776
- ```
777
-
778
- You can add ValidUntil and CacheDuration to the XML Metadata using instead
779
- ```ruby
780
- # Valid until => 2 days from now
781
- # Cache duration = 604800s = 1 week
782
- valid_until = Time.now + 172800
783
- cache_duration = 604800
784
- meta.generate(settings, false, valid_until, cache_duration)
785
- ```
786
-
787
935
  ## Clock Drift
788
936
 
789
937
  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.
@@ -798,13 +946,33 @@ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_cloc
798
946
 
799
947
  Make sure to keep the value as comfortably small as possible to keep security risks to a minimum.
800
948
 
949
+ ## Deflation Limit
950
+
951
+ To protect against decompression bombs (a form of DoS attack), SAML messages are limited to 250,000 bytes by default.
952
+ Sometimes legitimate SAML messages will exceed this limit,
953
+ for example due to custom claims like including groups a user is a member of.
954
+ If you want to customize this limit, you need to provide a different setting when initializing the response object.
955
+ Example:
956
+
957
+ ```ruby
958
+ def consume
959
+ response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], { settings: saml_settings })
960
+ ...
961
+ end
962
+
963
+ private
964
+
965
+ def saml_settings
966
+ OneLogin::RubySaml::Settings.new(message_max_bytesize: 500_000)
967
+ end
968
+ ```
969
+
801
970
  ## Attribute Service
802
971
 
803
- 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.
972
+ To request attributes from the IdP the SP must provide an attribute service within its metadata and reference the index in the assertion.
804
973
 
805
974
  ```ruby
806
975
  settings = OneLogin::RubySaml::Settings.new
807
-
808
976
  settings.attributes_index = 5
809
977
  settings.attribute_consuming_service.configure do
810
978
  service_name "Service"
@@ -815,3 +983,27 @@ end
815
983
  ```
816
984
 
817
985
  The `attribute_value` option additionally accepts an array of possible values.
986
+
987
+ ## Custom Metadata Fields
988
+
989
+ Some IdPs may require SPs to add additional fields (Organization, ContactPerson, etc.)
990
+ into the SP metadata. This can be achieved by extending the `OneLogin::RubySaml::Metadata`
991
+ class and overriding the `#add_extras` method as per the following example:
992
+
993
+ ```ruby
994
+ class MyMetadata < OneLogin::RubySaml::Metadata
995
+ def add_extras(root, _settings)
996
+ org = root.add_element("md:Organization")
997
+ org.add_element("md:OrganizationName", 'xml:lang' => "en-US").text = 'ACME Inc.'
998
+ org.add_element("md:OrganizationDisplayName", 'xml:lang' => "en-US").text = 'ACME'
999
+ org.add_element("md:OrganizationURL", 'xml:lang' => "en-US").text = 'https://www.acme.com'
1000
+
1001
+ cp = root.add_element("md:ContactPerson", 'contactType' => 'technical')
1002
+ cp.add_element("md:GivenName").text = 'ACME SAML Team'
1003
+ cp.add_element("md:EmailAddress").text = 'saml@acme.com'
1004
+ end
1005
+ end
1006
+
1007
+ # Output XML with custom metadata
1008
+ MyMetadata.new.generate(settings)
1009
+ ```