saml_idp 0.7.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +41 -13
  4. data/lib/saml_idp/configurator.rb +5 -1
  5. data/lib/saml_idp/controller.rb +9 -5
  6. data/lib/saml_idp/incoming_metadata.rb +22 -1
  7. data/lib/saml_idp/metadata_builder.rb +23 -8
  8. data/lib/saml_idp/persisted_metadata.rb +4 -0
  9. data/lib/saml_idp/request.rb +22 -3
  10. data/lib/saml_idp/response_builder.rb +19 -5
  11. data/lib/saml_idp/saml_response.rb +15 -3
  12. data/lib/saml_idp/service_provider.rb +15 -6
  13. data/lib/saml_idp/signable.rb +1 -2
  14. data/lib/saml_idp/version.rb +1 -1
  15. data/lib/saml_idp/xml_security.rb +1 -1
  16. data/saml_idp.gemspec +25 -23
  17. data/spec/acceptance/idp_controller_spec.rb +5 -4
  18. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  19. data/spec/lib/saml_idp/assertion_builder_spec.rb +8 -8
  20. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  21. data/spec/lib/saml_idp/configurator_spec.rb +8 -7
  22. data/spec/lib/saml_idp/controller_spec.rb +47 -20
  23. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  24. data/spec/lib/saml_idp/incoming_metadata_spec.rb +60 -0
  25. data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
  26. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  27. data/spec/lib/saml_idp/request_spec.rb +22 -22
  28. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  29. data/spec/lib/saml_idp/saml_response_spec.rb +31 -8
  30. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  31. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  32. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  33. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  34. data/spec/rails_app/app/controllers/saml_controller.rb +5 -1
  35. data/spec/rails_app/config/application.rb +0 -6
  36. data/spec/rails_app/config/environments/development.rb +1 -6
  37. data/spec/rails_app/config/environments/production.rb +1 -0
  38. data/spec/rails_app/config/environments/test.rb +1 -0
  39. data/spec/spec_helper.rb +22 -0
  40. data/spec/support/certificates/sp_cert_req.csr +12 -0
  41. data/spec/support/certificates/sp_private_key.pem +16 -0
  42. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  43. data/spec/support/saml_request_macros.rb +64 -4
  44. data/spec/support/security_helpers.rb +10 -0
  45. data/spec/xml_security_spec.rb +12 -12
  46. metadata +89 -52
  47. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 67d795e966607dc7c0553d2889e7da7e33b0ba92
4
- data.tar.gz: c5eb9c3ab9ae64be8ad5e412a94ad8600b329bbd
2
+ SHA256:
3
+ metadata.gz: f04deecaf7c0bd7c5655134d314a4b95b9438b24b67e83d7b160d9fa2232f2fc
4
+ data.tar.gz: b999a0a1f97e85e34704bfe35d3dddb89eebcfbfe1723be5e9dfcfb17e511ef5
5
5
  SHA512:
6
- metadata.gz: 4d41a8d82d518fc503b50d7206c1f12a59e822aa73dcd03f067d25521404446f66aba9d6c6ec478bfa7b9bb9794859c92d530e41cdae5ebcf5fe26e61f96e0a9
7
- data.tar.gz: c143f75f4326f1f1b927b91b5226797881d2bf2ea69155a4769d3805bd628d199f08edd5466529865cdffe57b2fc657cda3f100138a86aa3040af1e8682f0d10
6
+ metadata.gz: 94921b45008f31783c0428992b9cad6b4b1098ad312fd721987d0d27f89921f286f7bd8960237b5f371f8ccb23cac1a6c8b6c7aa110fcf4318a0b63b52497e9e
7
+ data.tar.gz: e142a4c38d3604dc033d0cfef0a298fbb094d5d36939518558aa219d6bd16ca753960fcc553c076e9969e031946e56d10ef3ba0c1505fcf9df3f7ee62ecdab11
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
  gemspec
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Ruby SAML Identity Provider (IdP)
2
+
2
3
  Forked from https://github.com/lawrencepit/ruby-saml-idp
3
4
 
4
- [![Build Status](https://travis-ci.org/sportngin/saml_idp.png)](https://travis-ci.org/sportngin/saml_idp)
5
- [![Gem Version](https://badge.fury.io/rb/saml_idp.png)](http://badge.fury.io/rb/saml_idp)
5
+ [![Build Status](https://travis-ci.org/saml-idp/saml_idp.svg)](https://travis-ci.org/saml-idp/saml_idp)
6
+ [![Gem Version](https://badge.fury.io/rb/saml_idp.svg)](http://badge.fury.io/rb/saml_idp)
6
7
 
7
8
  The ruby SAML Identity Provider library is for implementing the server side of SAML authentication. It allows
8
9
  your application to act as an IdP (Identity Provider) using the
@@ -19,6 +20,7 @@ Add this to your Gemfile:
19
20
  gem 'saml_idp'
20
21
 
21
22
  ## Not using rails?
23
+
22
24
  Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
23
25
 
24
26
  Basically you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value
@@ -30,9 +32,10 @@ posting to `saml_acs_url` the parameter `SAMLResponse` with the return value fro
30
32
  `encode_response(user_email)`.
31
33
 
32
34
  ## Using rails?
35
+
33
36
  Add to your `routes.rb` file, for example:
34
37
 
35
- ``` ruby
38
+ ```ruby
36
39
  get '/saml/auth' => 'saml_idp#new'
37
40
  get '/saml/metadata' => 'saml_idp#show'
38
41
  post '/saml/auth' => 'saml_idp#create'
@@ -41,7 +44,7 @@ match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
41
44
 
42
45
  Create a controller that looks like this, customize to your own situation:
43
46
 
44
- ``` ruby
47
+ ```ruby
45
48
  class SamlIdpController < SamlIdp::IdpController
46
49
  def idp_authenticate(email, password) # not using params intentionally
47
50
  user = User.by_email(email).first
@@ -69,6 +72,22 @@ end
69
72
 
70
73
  ## Configuration
71
74
 
75
+ #### Signed assertions and Signed Response
76
+
77
+ By default SAML Assertion will be signed with an algorithm which defined to `config.algorithm`. Because SAML assertions contain secure information used for authentication such as NameID.
78
+
79
+ Signing SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.
80
+ For that, you can enable it with `config.signed_message` option. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)
81
+
82
+ #### Signing algorithm
83
+
84
+ Following algorithms you can set in your response signing algorithm
85
+ :sha1 - RSA-SHA1 default value but not recommended to production environment
86
+ Highly recommended to use one of following algorithm, suit with your computing power.
87
+ :sha256 - RSA-SHA256
88
+ :sha384 - RSA-SHA384
89
+ :sha512 - RSA-SHA512
90
+
72
91
  Be sure to load a file like this during your app initialization:
73
92
 
74
93
  ```ruby
@@ -88,18 +107,21 @@ KEY DATA
88
107
  CERT
89
108
 
90
109
  # config.password = "secret_key_password"
91
- # config.algorithm = :sha256
110
+ # config.algorithm = :sha256 # Default: sha1 only for development.
92
111
  # config.organization_name = "Your Organization"
93
112
  # config.organization_url = "http://example.com"
94
113
  # config.base_saml_location = "#{base}/saml"
95
- # config.reference_id_generator # Default: -> { UUID.generate }
114
+ # config.reference_id_generator # Default: -> { SecureRandom.uuid }
115
+ # config.single_logout_service_post_location = "#{base}/saml/logout"
116
+ # config.single_logout_service_redirect_location = "#{base}/saml/logout"
96
117
  # config.attribute_service_location = "#{base}/saml/attributes"
97
118
  # config.single_service_post_location = "#{base}/saml/auth"
98
119
  # config.session_expiry = 86400 # Default: 0 which means never
120
+ # config.signed_message = true # Default: false which means unsigned SAML Response
99
121
 
100
122
  # Principal (e.g. User) is passed in when you `encode_response`
101
123
  #
102
- # config.name_id.formats # =>
124
+ # config.name_id.formats =
103
125
  # { # All 2.0
104
126
  # email_address: -> (principal) { principal.email_address },
105
127
  # transient: -> (principal) { principal.id },
@@ -169,7 +191,11 @@ CERT
169
191
  service_providers = {
170
192
  "some-issuer-url.com/saml" => {
171
193
  fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D",
172
- metadata_url: "http://some-issuer-url.com/saml/metadata"
194
+ metadata_url: "http://some-issuer-url.com/saml/metadata",
195
+
196
+ # We now validate AssertionConsumerServiceURL will match the MetadataURL set above.
197
+ # *If* it's not going to match your Metadata URL's Host, then set this so we can validate the host using this list
198
+ response_hosts: ["foo.some-issuer-url.com"]
173
199
  },
174
200
  }
175
201
 
@@ -177,7 +203,7 @@ CERT
177
203
  # settings is an IncomingMetadata object which has a to_h method that needs to be persisted
178
204
  config.service_provider.metadata_persister = ->(identifier, settings) {
179
205
  fname = identifier.to_s.gsub(/\/|:/,"_")
180
- `mkdir -p #{Rails.root.join("cache/saml/metadata")}`
206
+ FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)
181
207
  File.open Rails.root.join("cache/saml/metadata/#{fname}"), "r+b" do |f|
182
208
  Marshal.dump settings.to_h, f
183
209
  end
@@ -188,7 +214,7 @@ CERT
188
214
  # `service_provider` you should return the settings.to_h from above
189
215
  config.service_provider.persisted_metadata_getter = ->(identifier, service_provider){
190
216
  fname = identifier.to_s.gsub(/\/|:/,"_")
191
- `mkdir -p #{Rails.root.join("cache/saml/metadata")}`
217
+ FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)
192
218
  full_filename = Rails.root.join("cache/saml/metadata/#{fname}")
193
219
  if File.file?(full_filename)
194
220
  File.open full_filename, "rb" do |f|
@@ -205,6 +231,7 @@ end
205
231
  ```
206
232
 
207
233
  # Keys and Secrets
234
+
208
235
  To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
209
236
  You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
210
237
  Obviously you shouldn't use these if you intend to use this in production environments. In that case,
@@ -218,18 +245,19 @@ The fingerprint to use, if you use the default X.509 certificate of this gem, is
218
245
  9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
219
246
  ```
220
247
 
221
-
222
248
  # Service Providers
249
+
223
250
  To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
224
251
  excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
225
252
 
226
-
227
253
  # Author
228
- Jon Phenow, me@jphenow.com
254
+
255
+ Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
229
256
 
230
257
  Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
231
258
 
232
259
  # Copyright
260
+
233
261
  Copyright (c) 2012 Sport Ngin.
234
262
  Portions Copyright (c) 2010 OneLogin, LLC
235
263
  Portions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require 'ostruct'
3
+ require 'securerandom'
4
+
3
5
  module SamlIdp
4
6
  class Configurator
5
7
  attr_accessor :x509_certificate
@@ -13,17 +15,19 @@ module SamlIdp
13
15
  attr_accessor :reference_id_generator
14
16
  attr_accessor :attribute_service_location
15
17
  attr_accessor :single_service_post_location
18
+ attr_accessor :single_service_redirect_location
16
19
  attr_accessor :single_logout_service_post_location
17
20
  attr_accessor :single_logout_service_redirect_location
18
21
  attr_accessor :attributes
19
22
  attr_accessor :service_provider
23
+ attr_accessor :assertion_consumer_service_hosts
20
24
  attr_accessor :session_expiry
21
25
 
22
26
  def initialize
23
27
  self.x509_certificate = Default::X509_CERTIFICATE
24
28
  self.secret_key = Default::SECRET_KEY
25
29
  self.algorithm = :sha1
26
- self.reference_id_generator = ->() { UUID.generate }
30
+ self.reference_id_generator = ->() { SecureRandom.uuid }
27
31
  self.service_provider = OpenStruct.new
28
32
  self.service_provider.finder = ->(_) { Default::SERVICE_PROVIDER }
29
33
  self.service_provider.metadata_persister = ->(id, settings) { }
@@ -2,7 +2,7 @@
2
2
  require 'openssl'
3
3
  require 'base64'
4
4
  require 'time'
5
- require 'uuid'
5
+ require 'securerandom'
6
6
  require 'saml_idp/request'
7
7
  require 'saml_idp/logout_response_builder'
8
8
  module SamlIdp
@@ -35,13 +35,15 @@ module SamlIdp
35
35
 
36
36
  def validate_saml_request(raw_saml_request = params[:SAMLRequest])
37
37
  decode_request(raw_saml_request)
38
- unless valid_saml_request?
38
+ return true if valid_saml_request?
39
+ if defined?(::Rails)
39
40
  if Rails::VERSION::MAJOR >= 4
40
41
  head :forbidden
41
42
  else
42
43
  render nothing: true, status: :forbidden
43
44
  end
44
45
  end
46
+ false
45
47
  end
46
48
 
47
49
  def decode_request(raw_saml_request)
@@ -62,6 +64,7 @@ module SamlIdp
62
64
  expiry = opts[:expiry] || 60*60
63
65
  session_expiry = opts[:session_expiry]
64
66
  encryption_opts = opts[:encryption] || nil
67
+ signed_message_opts = opts[:signed_message] || false
65
68
 
66
69
  SamlResponse.new(
67
70
  reference_id,
@@ -75,7 +78,8 @@ module SamlIdp
75
78
  my_authn_context_classref,
76
79
  expiry,
77
80
  encryption_opts,
78
- session_expiry
81
+ session_expiry,
82
+ signed_message_opts
79
83
  ).build
80
84
  end
81
85
 
@@ -122,11 +126,11 @@ module SamlIdp
122
126
  end
123
127
 
124
128
  def get_saml_response_id
125
- UUID.generate
129
+ SecureRandom.uuid
126
130
  end
127
131
 
128
132
  def get_saml_reference_id
129
- UUID.generate
133
+ SecureRandom.uuid
130
134
  end
131
135
 
132
136
  def default_algorithm
@@ -16,16 +16,37 @@ module SamlIdp
16
16
  @document ||= Saml::XML::Document.parse raw
17
17
  end
18
18
 
19
+ def entity_id
20
+ xpath('//md:EntityDescriptor/@entityID', md: metadata_namespace).first.try(:content).to_s
21
+ end
22
+ hashable :entity_id
23
+
19
24
  def sign_assertions
20
25
  doc = xpath(
21
26
  "//md:SPSSODescriptor",
22
27
  ds: signature_namespace,
23
28
  md: metadata_namespace
24
29
  ).first
25
- doc ? !!doc["WantAssertionsSigned"] : false
30
+ if (doc && !doc['WantAssertionsSigned'].nil?)
31
+ return doc['WantAssertionsSigned'].strip.downcase == 'true'
32
+ end
33
+ return false
26
34
  end
27
35
  hashable :sign_assertions
28
36
 
37
+ def sign_authn_request
38
+ doc = xpath(
39
+ "//md:SPSSODescriptor",
40
+ ds: signature_namespace,
41
+ md: metadata_namespace
42
+ ).first
43
+ if (doc && !doc['AuthnRequestsSigned'].nil?)
44
+ return doc['AuthnRequestsSigned'].strip.downcase == 'true'
45
+ end
46
+ return false
47
+ end
48
+ hashable :sign_authn_request
49
+
29
50
  def display_name
30
51
  role_descriptor_document.present? ? role_descriptor_document["ServiceDisplayName"] : ""
31
52
  end
@@ -24,13 +24,15 @@ module SamlIdp
24
24
 
25
25
  entity.IDPSSODescriptor protocolSupportEnumeration: protocol_enumeration do |descriptor|
26
26
  build_key_descriptor descriptor
27
- descriptor.SingleLogoutService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
28
- Location: single_logout_service_post_location
29
- descriptor.SingleLogoutService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
30
- Location: single_logout_service_redirect_location
27
+ build_endpoint descriptor, [
28
+ { tag: 'SingleLogoutService', url: single_logout_service_post_location, bind: 'HTTP-POST' },
29
+ { tag: 'SingleLogoutService', url: single_logout_service_redirect_location, bind: 'HTTP-Redirect'}
30
+ ]
31
31
  build_name_id_formats descriptor
32
- descriptor.SingleSignOnService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
33
- Location: single_service_post_location
32
+ build_endpoint descriptor, [
33
+ { tag: 'SingleSignOnService', url: single_service_post_location, bind: 'HTTP-POST' },
34
+ { tag: 'SingleSignOnService', url: single_service_redirect_location, bind: 'HTTP-Redirect'}
35
+ ]
34
36
  build_attribute descriptor
35
37
  end
36
38
 
@@ -38,8 +40,9 @@ module SamlIdp
38
40
  build_key_descriptor authority_descriptor
39
41
  build_organization authority_descriptor
40
42
  build_contact authority_descriptor
41
- authority_descriptor.AttributeService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
42
- Location: attribute_service_location
43
+ build_endpoint authority_descriptor, [
44
+ { tag: 'AttributeService', url: attribute_service_location, bind: 'HTTP-Redirect' }
45
+ ]
43
46
  build_name_id_formats authority_descriptor
44
47
  build_attribute authority_descriptor
45
48
  end
@@ -69,6 +72,17 @@ module SamlIdp
69
72
  end
70
73
  private :build_name_id_formats
71
74
 
75
+ def build_endpoint(el, end_points)
76
+ end_points.each do |ep|
77
+ next unless ep[:url].present?
78
+
79
+ el.tag! ep[:tag],
80
+ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:#{ep[:bind]}",
81
+ Location: ep[:url]
82
+ end
83
+ end
84
+ private :build_endpoint
85
+
72
86
  def build_attribute(el)
73
87
  attributes.each do |attribute|
74
88
  el.tag! "saml:Attribute",
@@ -151,6 +165,7 @@ module SamlIdp
151
165
  organization_url
152
166
  attribute_service_location
153
167
  single_service_post_location
168
+ single_service_redirect_location
154
169
  single_logout_service_post_location
155
170
  single_logout_service_redirect_location
156
171
  technical_contact
@@ -6,5 +6,9 @@ module SamlIdp
6
6
  def sign_assertions?
7
7
  !!attributes[:sign_assertions]
8
8
  end
9
+
10
+ def sign_authn_request?
11
+ !!attributes[:sign_authn_request]
12
+ end
9
13
  end
10
14
  end
@@ -105,13 +105,24 @@ module SamlIdp
105
105
  return false
106
106
  end
107
107
 
108
+ if !service_provider.acceptable_response_hosts.include?(response_host)
109
+ log "#{service_provider.acceptable_response_hosts} compare to #{response_host}"
110
+ log "No acceptable AssertionConsumerServiceURL, either configure them via config.service_provider.response_hosts or match to your metadata_url host"
111
+ return false
112
+ end
113
+
108
114
  return true
109
115
  end
110
116
 
111
117
  def valid_signature?
112
- # Force signatures for logout requests because there is no other
113
- # protection against a cross-site DoS.
114
- service_provider.valid_signature?(document, logout_request?)
118
+ # Force signatures for logout requests because there is no other protection against a cross-site DoS.
119
+ # Validate signature when metadata specify AuthnRequest should be signed
120
+ metadata = service_provider.current_metadata
121
+ if logout_request? || authn_request? && metadata.respond_to?(:sign_authn_request?) && metadata.sign_authn_request?
122
+ document.valid_signature?(service_provider.fingerprint)
123
+ else
124
+ true
125
+ end
115
126
  end
116
127
 
117
128
  def service_provider?
@@ -136,6 +147,14 @@ module SamlIdp
136
147
  @_session_index ||= xpath("//samlp:SessionIndex", samlp: samlp).first.try(:content)
137
148
  end
138
149
 
150
+ def response_host
151
+ uri = URI(response_url)
152
+ if uri
153
+ uri.host
154
+ end
155
+ end
156
+ private :response_host
157
+
139
158
  def document
140
159
  @_document ||= Saml::XML::Document.parse(raw_xml)
141
160
  end
@@ -1,32 +1,45 @@
1
1
  require 'builder'
2
+ require 'saml_idp/algorithmable'
3
+ require 'saml_idp/signable'
2
4
  module SamlIdp
3
5
  class ResponseBuilder
6
+ include Algorithmable
7
+ include Signable
4
8
  attr_accessor :response_id
5
9
  attr_accessor :issuer_uri
6
10
  attr_accessor :saml_acs_url
7
11
  attr_accessor :saml_request_id
8
12
  attr_accessor :assertion_and_signature
13
+ attr_accessor :raw_algorithm
9
14
 
10
- def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature)
15
+ alias_method :reference_id, :response_id
16
+
17
+ def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature, raw_algorithm)
11
18
  self.response_id = response_id
12
19
  self.issuer_uri = issuer_uri
13
20
  self.saml_acs_url = saml_acs_url
14
21
  self.saml_request_id = saml_request_id
15
22
  self.assertion_and_signature = assertion_and_signature
23
+ self.raw_algorithm = raw_algorithm
16
24
  end
17
25
 
18
- def encoded
19
- @encoded ||= encode
26
+ def encoded(signed_message: false)
27
+ @encoded ||= signed_message ? encode_signed_message : encode_raw_message
20
28
  end
21
29
 
22
30
  def raw
23
31
  build
24
32
  end
25
33
 
26
- def encode
34
+ def encode_raw_message
27
35
  Base64.strict_encode64(raw)
28
36
  end
29
- private :encode
37
+ private :encode_raw_message
38
+
39
+ def encode_signed_message
40
+ Base64.strict_encode64(signed)
41
+ end
42
+ private :encode_signed_message
30
43
 
31
44
  def build
32
45
  resp_options = {}
@@ -41,6 +54,7 @@ module SamlIdp
41
54
  builder = Builder::XmlMarkup.new
42
55
  builder.tag! "samlp:Response", resp_options do |response|
43
56
  response.Issuer issuer_uri, xmlns: Saml::XML::Namespaces::ASSERTION
57
+ sign response
44
58
  response.tag! "samlp:Status" do |status|
45
59
  status.tag! "samlp:StatusCode", Value: Saml::XML::Namespaces::Statuses::SUCCESS
46
60
  end