libsaml 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +91 -0
  4. data/Rakefile +33 -0
  5. data/lib/saml.rb +142 -0
  6. data/lib/saml/artifact.rb +51 -0
  7. data/lib/saml/artifact_resolve.rb +10 -0
  8. data/lib/saml/artifact_response.rb +9 -0
  9. data/lib/saml/assertion.rb +67 -0
  10. data/lib/saml/authn_request.rb +34 -0
  11. data/lib/saml/base.rb +47 -0
  12. data/lib/saml/bindings/http_artifact.rb +44 -0
  13. data/lib/saml/bindings/http_post.rb +29 -0
  14. data/lib/saml/bindings/http_redirect.rb +100 -0
  15. data/lib/saml/bindings/soap.rb +31 -0
  16. data/lib/saml/complex_types/endpoint_type.rb +17 -0
  17. data/lib/saml/complex_types/indexed_endpoint_type.rb +15 -0
  18. data/lib/saml/complex_types/request_abstract_type.rb +57 -0
  19. data/lib/saml/complex_types/sso_descriptor_type.rb +48 -0
  20. data/lib/saml/complex_types/status_response_type.rb +29 -0
  21. data/lib/saml/config.rb +49 -0
  22. data/lib/saml/elements/attribute.rb +24 -0
  23. data/lib/saml/elements/attribute_statement.rb +26 -0
  24. data/lib/saml/elements/audience_restriction.rb +12 -0
  25. data/lib/saml/elements/authn_context.rb +13 -0
  26. data/lib/saml/elements/authn_statement.rb +25 -0
  27. data/lib/saml/elements/conditions.rb +24 -0
  28. data/lib/saml/elements/contact_person.rb +33 -0
  29. data/lib/saml/elements/entities_descriptor.rb +27 -0
  30. data/lib/saml/elements/entity_descriptor.rb +37 -0
  31. data/lib/saml/elements/idp_sso_descriptor.rb +23 -0
  32. data/lib/saml/elements/key_descriptor.rb +34 -0
  33. data/lib/saml/elements/key_descriptor/key_info.rb +30 -0
  34. data/lib/saml/elements/key_descriptor/key_info/x509_data.rb +34 -0
  35. data/lib/saml/elements/name_id.rb +14 -0
  36. data/lib/saml/elements/organization.rb +16 -0
  37. data/lib/saml/elements/requested_authn_context.rb +28 -0
  38. data/lib/saml/elements/signature.rb +33 -0
  39. data/lib/saml/elements/signature/canonicalization_method.rb +19 -0
  40. data/lib/saml/elements/signature/digest_method.rb +19 -0
  41. data/lib/saml/elements/signature/inclusive_namespaces.rb +20 -0
  42. data/lib/saml/elements/signature/key_info.rb +14 -0
  43. data/lib/saml/elements/signature/reference.rb +23 -0
  44. data/lib/saml/elements/signature/signature_method.rb +19 -0
  45. data/lib/saml/elements/signature/signed_info.rb +24 -0
  46. data/lib/saml/elements/signature/transform.rb +19 -0
  47. data/lib/saml/elements/signature/transforms.rb +21 -0
  48. data/lib/saml/elements/sp_sso_descriptor.rb +27 -0
  49. data/lib/saml/elements/status.rb +15 -0
  50. data/lib/saml/elements/status_code.rb +42 -0
  51. data/lib/saml/elements/sub_status_code.rb +14 -0
  52. data/lib/saml/elements/subject.rb +38 -0
  53. data/lib/saml/elements/subject_confirmation.rb +30 -0
  54. data/lib/saml/elements/subject_confirmation_data.rb +23 -0
  55. data/lib/saml/elements/subject_locality.rb +12 -0
  56. data/lib/saml/encoding.rb +35 -0
  57. data/lib/saml/logout_request.rb +10 -0
  58. data/lib/saml/logout_response.rb +11 -0
  59. data/lib/saml/provider.rb +85 -0
  60. data/lib/saml/provider_stores/file.rb +33 -0
  61. data/lib/saml/response.rb +21 -0
  62. data/lib/saml/util.rb +51 -0
  63. data/lib/saml/version.rb +3 -0
  64. data/lib/saml/xml_helpers.rb +34 -0
  65. data/lib/tasks/saml_tasks.rake +4 -0
  66. metadata +195 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzdjMGJhZTcxYTc2MTUxMmNlMTdhNzEzNDlmOGI4ODEyZDQxMTBhNg==
5
+ data.tar.gz: !binary |-
6
+ NTY3YjkxMDI3OTQyNjUyYTViZDc5MTAxZTBjYzM4ZjllOGVkYzM0Mg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NDcwYjA1YTZmZDcyZGVhZmVmNDlhMjhmMWQ1ZDRiNGIzNzU0YzIzMTE2Y2U5
10
+ ZmUxYmUyYTBkNGVkN2ZhMTZlYjNjZjgzNjE2ZmZmOTIwMzIyNzA0MWQxMjdh
11
+ M2FkY2NjODEyYjdkMTIwNzY5NzVmYmI3MmJhZTIyMzdhZTM2MTE=
12
+ data.tar.gz: !binary |-
13
+ ODdjOTk5ZjJhZWZjMmFiN2M3NWFlYzA4N2Y3Njk2OWUxYTk3YzFmMGZjNTZj
14
+ Y2YwYzcwYzI2YWM0YzNhZjc4NzE4Y2UzYTRjZjBhMzZmNjQ4NjgzNWY4NDU2
15
+ Y2ZjODYxNWE4YTBkMDhmNjY5ZDJjZjQ1NGIzN2E2NzM4ZDJhNWY=
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Digidentity
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,91 @@
1
+ {<img src="https://travis-ci.org/digidentity/libsaml.png?branch=master" alt="Build Status" />}[https://travis-ci.org/digidentity/libsaml]
2
+ = libsaml
3
+ Libsaml is a Ruby gem to easily create SAML 2.0 messages. This gem was written because other SAML gems were missing functionality such as XML signing.
4
+
5
+ Libsaml's features include:
6
+ - Bindings: HTTP-Post, HTTP-Redirect, HTTP-Artifact, SOAP
7
+ - XML signing and verification
8
+ - Pluggable backend for providers (FileStore backend included)
9
+
10
+ Copyright Digidentity BV, released under the MIT license. This gem was written by Benoist Claassen.
11
+
12
+ = Installation
13
+
14
+ Place in your Gemfile:
15
+ gem 'libsaml', require: 'saml'
16
+
17
+ = Usage
18
+ Below follows how to configure the SAML gem in a service provider.
19
+
20
+ Store the private key in:
21
+ config/ssl/key.pem
22
+
23
+ Store the public key of the identity provider in:
24
+ config/ssl/trust-federate.cert
25
+
26
+ Add the Identity Provider web container configuration file to config/metadata/service_provider.xml.
27
+ This contains an encoded version of the public key, generate this in the ruby console by typing:
28
+ irb
29
+ require 'openssl'
30
+ require 'base64'
31
+ pem = File.open("config/ssl/trust-federate.cert").read
32
+ cert = OpenSSL::X509::Certificate.new(pem)
33
+ output = Base64.encode64(cert.to_der).gsub("\n", "")
34
+
35
+ Add the Service Provider configuration file to config/metadata/service_provider.xml:
36
+ <?xml version="1.0" encoding="UTF-8"?>
37
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" ID="_052c51476c9560a429e1171e8c9528b96b69fb57" entityID="my:very:original:entityid">
38
+ <md:SPSSODescriptor>
39
+ <md:KeyDescriptor use="signing">
40
+ <ds:KeyInfo>
41
+ <ds:X509Data>
42
+ <ds:X509Certificate>SAME_KEY_AS_GENERATED_IN_THE_CONSOLE_BEFORE</ds:X509Certificate>
43
+ </ds:X509Data>
44
+ </ds:KeyInfo>
45
+ </md:KeyDescriptor>
46
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Post" index="0" Location="http://localhost:3000/saml/receive_response" isDefault="true"/>
47
+ </md:SPSSODescriptor>
48
+ </md:EntityDescriptor>
49
+
50
+ Set up an intializer in config/initializers/saml_config.rb:
51
+ Saml.setup do |config|
52
+ config.entity_id = "my:very:original:entityid"
53
+ config.provider_store = Saml::ProviderStore::File.new("config/metadata", "config/ssl/key.pem")
54
+ # config.provider_store = SamlProvider
55
+ end
56
+
57
+ By default this will use a SamlProvider model that uses the filestore, if you want a database driven model comment out the #provider_store function in the initializer and make a model that defines #find_by_entity_id:
58
+ class SamlProvider < ActiveRecord::Base
59
+ include Saml::Provider
60
+
61
+ def self.find_by_entity_id(entity_id)
62
+ where(entity_id: entity_id).first!
63
+ end
64
+ end
65
+
66
+
67
+ Now you can make a SAML controller in app/controllers/saml_controller.rb:
68
+ class SamlController < ApplicationController
69
+ def request_authentication
70
+ provider = Saml.provider("my:very:original:entityid")
71
+ destination = provider.single_sign_on_service_url(Saml::ProtocolBindings::HTTP_POST)
72
+
73
+ authn_request = Saml::AuthnRequest.new(:destination => destination)
74
+
75
+ @saml_attributes = Saml::Bindings::HTTPPost.create_form_attributes(authn_request)
76
+
77
+ render text: @saml_attributes.to_yaml
78
+ end
79
+
80
+ def receive_response
81
+ end
82
+ end
83
+
84
+ Don't forget to define the routes in config/routes.rb:
85
+ get "/saml/request_authentication" => "saml#request_authentication"
86
+ get "/saml/receive_response" => "saml#receive_response"
87
+
88
+ = Contributing
89
+ - Fork the project
90
+ - Contribute your changes. Please make sure your changes are properly documented and covered by tests.
91
+ - Send a pull request
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Saml'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ require "rspec/core/rake_task"
24
+
25
+ RSpec::Core::RakeTask.new(:core) do |spec|
26
+ spec.rspec_opts = ['--backtrace']
27
+ end
28
+
29
+ task :default => [:core]
30
+
31
+
32
+ Bundler::GemHelper.install_tasks
33
+
data/lib/saml.rb ADDED
@@ -0,0 +1,142 @@
1
+ require 'active_support/all'
2
+ require 'active_model'
3
+ require 'saml/base'
4
+ require 'saml/xml_helpers'
5
+ require 'saml/encoding'
6
+ require 'saml/util'
7
+ require 'xmldsig'
8
+ require 'httpi'
9
+
10
+ module Saml
11
+ MD_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:metadata'
12
+ SAML_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion'
13
+ SAMLP_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:protocol'
14
+ XML_DSIG_NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
15
+ SAML_VERSION = '2.0'
16
+
17
+ module Errors
18
+ class SamlError < StandardError;
19
+ end
20
+
21
+ class SignatureInvalid < SamlError;
22
+ end
23
+ class InvalidProvider < SamlError;
24
+ end
25
+ class UnparseableMessage < SamlError;
26
+ end
27
+ end
28
+
29
+ module TopLevelCodes
30
+ SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'
31
+ REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'
32
+ RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'
33
+ VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'
34
+
35
+ ALL = [SUCCESS, REQUESTER, RESPONDER, VERSION_MISMATCH]
36
+ end
37
+
38
+ module SubStatusCodes
39
+ AUTHN_FAILED = 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed'
40
+ NO_AUTHN_CONTEXT = 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext'
41
+ PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'
42
+ REQUEST_DENIED = 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied'
43
+
44
+ ALL = [AUTHN_FAILED, NO_AUTHN_CONTEXT, PARTIAL_LOGOUT, REQUEST_DENIED]
45
+ end
46
+
47
+ module Bindings
48
+ require 'saml/bindings/http_artifact'
49
+ require 'saml/bindings/http_redirect'
50
+ require 'saml/bindings/http_post'
51
+ require 'saml/bindings/soap'
52
+ end
53
+
54
+ module ClassRefs
55
+ PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
56
+ MOBILE_TWO_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'
57
+ MOBILE_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
58
+
59
+ ALL_CLASS_REFS = [PASSWORD_PROTECTED,
60
+ MOBILE_TWO_FACTOR_CONTRACT,
61
+ MOBILE_SMARTCARD_PKI]
62
+ ORDERED_CLASS_REFS = ALL_CLASS_REFS
63
+ end
64
+
65
+ module ComplexTypes
66
+ require 'saml/complex_types/request_abstract_type'
67
+ require 'saml/complex_types/status_response_type'
68
+ require 'saml/complex_types/endpoint_type'
69
+ require 'saml/complex_types/indexed_endpoint_type'
70
+ require 'saml/complex_types/sso_descriptor_type'
71
+ end
72
+
73
+ module Elements
74
+ require 'saml/elements/signature'
75
+ require 'saml/elements/subject_locality'
76
+ require 'saml/elements/authn_context'
77
+ require 'saml/elements/audience_restriction'
78
+ require 'saml/elements/sub_status_code'
79
+ require 'saml/elements/status_code'
80
+ require 'saml/elements/status'
81
+ require 'saml/elements/subject_confirmation_data'
82
+ require 'saml/elements/subject_confirmation'
83
+ require 'saml/elements/attribute'
84
+ require 'saml/elements/attribute_statement'
85
+ require 'saml/elements/name_id'
86
+ require 'saml/elements/subject'
87
+ require 'saml/elements/conditions'
88
+ require 'saml/elements/authn_statement'
89
+ require 'saml/elements/requested_authn_context'
90
+ require 'saml/elements/key_descriptor'
91
+ require 'saml/elements/organization'
92
+ require 'saml/elements/contact_person'
93
+ require 'saml/elements/idp_sso_descriptor'
94
+ require 'saml/elements/sp_sso_descriptor'
95
+ require 'saml/elements/entity_descriptor'
96
+ require 'saml/elements/entities_descriptor'
97
+ end
98
+
99
+ require 'saml/assertion'
100
+ require 'saml/authn_request'
101
+ require 'saml/artifact'
102
+ require 'saml/response'
103
+ require 'saml/artifact_resolve'
104
+ require 'saml/artifact_response'
105
+ require 'saml/logout_request'
106
+ require 'saml/logout_response'
107
+ require 'saml/provider'
108
+
109
+ module ProviderStores
110
+ require 'saml/provider_stores/file'
111
+ end
112
+
113
+ module ProtocolBinding
114
+ HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
115
+ HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
116
+ HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
117
+ SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'
118
+ end
119
+
120
+ def self.setup
121
+ yield Saml::Config
122
+ end
123
+
124
+ def self.generate_id
125
+ "_#{::SecureRandom.hex(20)}"
126
+ end
127
+
128
+ def self.provider(entity_id)
129
+ Saml::Config.provider_store.find_by_entity_id(entity_id) || raise(Saml::Errors::InvalidProvider.new)
130
+ end
131
+
132
+ def self.parse_message(message, type)
133
+ if %w(authn_request response logout_request logout_response artifact_resolve artifact_response).include?(type.to_s)
134
+ klass = "Saml::#{type.to_s.camelize}".constantize
135
+ klass.parse(message, single: true)
136
+ else
137
+ nil
138
+ end
139
+ end
140
+ end
141
+
142
+ require 'saml/config'
@@ -0,0 +1,51 @@
1
+ module Saml
2
+ class Artifact
3
+ include ::HappyMapper
4
+
5
+ TYPE_CODE = "\000\004"
6
+ END_POINT_INDEX = "\000\000"
7
+
8
+ tag "Artifact"
9
+ namespace 'samlp'
10
+
11
+ content :artifact, String
12
+
13
+ def initialize(artifact = nil)
14
+ if artifact
15
+ @artifact = artifact
16
+ else
17
+ source_id = ::Digest::SHA1.digest(Saml::Config.entity_id)
18
+ message_handle = ::SecureRandom.random_bytes(20)
19
+ @type_code = TYPE_CODE
20
+ @endpoint_index = END_POINT_INDEX
21
+ @artifact = Saml::Encoding.encode_64 [@type_code, @endpoint_index, source_id, message_handle].join
22
+ end
23
+ end
24
+
25
+ def type_code
26
+ decoded_value[0, 2]
27
+ end
28
+
29
+ def endpoint_index
30
+ decoded_value[2, 2]
31
+ end
32
+
33
+ def source_id
34
+ decoded_value[4, 20]
35
+ end
36
+
37
+ def message_handle
38
+ decoded_value[24, 20]
39
+ end
40
+
41
+ def to_s
42
+ artifact
43
+ end
44
+
45
+ private
46
+
47
+ def decoded_value
48
+ ::Base64.decode64(artifact)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ module Saml
2
+ class ArtifactResolve
3
+ include Saml::ComplexTypes::RequestAbstractType
4
+
5
+ tag "ArtifactResolve"
6
+ has_one :artifact, Saml::Artifact
7
+
8
+ validates :artifact, :presence => true
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Saml
2
+ class ArtifactResponse
3
+ include Saml::ComplexTypes::StatusResponseType
4
+
5
+ tag "ArtifactResponse"
6
+
7
+ has_one :response, Saml::Response
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ module Saml
2
+ class Assertion
3
+ include Saml::Base
4
+ include Saml::XMLHelpers
5
+
6
+ register_namespace 'samlp', Saml::SAMLP_NAMESPACE
7
+ register_namespace 'saml', Saml::SAML_NAMESPACE
8
+
9
+ tag "Assertion"
10
+ namespace 'saml'
11
+
12
+ attribute :_id, String, :tag => 'ID'
13
+ attribute :version, String, :tag => "Version"
14
+ attribute :issue_instant, Time, :tag => "IssueInstant", :on_save => lambda { |val| val.utc.xmlschema }
15
+
16
+ element :issuer, String, :namespace => 'saml', :tag => "Issuer"
17
+
18
+ has_one :signature, Saml::Elements::Signature
19
+ has_one :subject, Saml::Elements::Subject
20
+ has_one :conditions, Saml::Elements::Conditions
21
+ has_many :authn_statement, Saml::Elements::AuthnStatement
22
+ has_one :attribute_statement, Saml::Elements::AttributeStatement
23
+
24
+ validates :_id, :version, :issue_instant, :issuer, :presence => true
25
+
26
+ validates :version, inclusion: %w(2.0)
27
+ validate :check_issue_instant, :if => "issue_instant.present?"
28
+
29
+ def initialize(*args)
30
+ options = args.extract_options!
31
+ @subject = Saml::Elements::Subject.new(:name_id => options.delete(:name_id),
32
+ :name_id_format => options.delete(:name_id_format),
33
+ :recipient => options.delete(:recipient),
34
+ :in_response_to => options.delete(:in_response_to))
35
+ @conditions = Saml::Elements::Conditions.new(:audience => options.delete(:audience))
36
+ @authn_statement = Saml::Elements::AuthnStatement.new(:authn_instant => Time.now,
37
+ :address => options.delete(:address),
38
+ :authn_context_class_ref => options.delete(:authn_context_class_ref),
39
+ :session_index => options.delete(:session_index))
40
+ super(*(args << options))
41
+ @_id ||= Saml.generate_id
42
+ @issue_instant ||= Time.now
43
+ @issuer ||= Saml::Config.entity_id
44
+ @version ||= Saml::SAML_VERSION
45
+ end
46
+
47
+ def add_attribute(key, value)
48
+ self.attribute_statement ||= Saml::Elements::AttributeStatement.new
49
+ self.attribute_statement.attribute ||= []
50
+ self.attribute_statement.attribute << Saml::Elements::Attribute.new(name: key, attribute_value: value)
51
+ end
52
+
53
+ def fetch_attribute(key)
54
+ return unless self.attribute_statement
55
+ return unless self.attribute_statement.attribute
56
+ attribute_statement.fetch_attribute(key)
57
+ end
58
+
59
+ private
60
+
61
+ def check_issue_instant
62
+ errors.add(:issue_instant, :too_old) if issue_instant < Time.now - Saml::Config.max_issue_instant_offset.minutes
63
+ errors.add(:issue_instant, :too_new) if issue_instant > Time.now + Saml::Config.max_issue_instant_offset.minutes
64
+ end
65
+
66
+ end
67
+ end