custom-adal 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +106 -0
- data/Rakefile +39 -0
- data/adal.gemspec +52 -0
- data/contributing.md +127 -0
- data/lib/adal/authentication_context.rb +202 -0
- data/lib/adal/authentication_parameters.rb +126 -0
- data/lib/adal/authority.rb +165 -0
- data/lib/adal/cache_driver.rb +171 -0
- data/lib/adal/cached_token_response.rb +190 -0
- data/lib/adal/client_assertion.rb +63 -0
- data/lib/adal/client_assertion_certificate.rb +89 -0
- data/lib/adal/client_credential.rb +46 -0
- data/lib/adal/core_ext/hash.rb +34 -0
- data/lib/adal/core_ext.rb +26 -0
- data/lib/adal/jwt_parameters.rb +39 -0
- data/lib/adal/logger.rb +90 -0
- data/lib/adal/logging.rb +98 -0
- data/lib/adal/memory_cache.rb +95 -0
- data/lib/adal/mex_request.rb +52 -0
- data/lib/adal/mex_response.rb +141 -0
- data/lib/adal/noop_cache.rb +38 -0
- data/lib/adal/oauth_request.rb +76 -0
- data/lib/adal/request_parameters.rb +48 -0
- data/lib/adal/self_signed_jwt_factory.rb +96 -0
- data/lib/adal/templates/rst.13.xml.erb +35 -0
- data/lib/adal/templates/rst.2005.xml.erb +32 -0
- data/lib/adal/token_request.rb +231 -0
- data/lib/adal/token_response.rb +144 -0
- data/lib/adal/user_assertion.rb +57 -0
- data/lib/adal/user_credential.rb +152 -0
- data/lib/adal/user_identifier.rb +83 -0
- data/lib/adal/user_information.rb +49 -0
- data/lib/adal/util.rb +49 -0
- data/lib/adal/version.rb +36 -0
- data/lib/adal/wstrust_request.rb +100 -0
- data/lib/adal/wstrust_response.rb +168 -0
- data/lib/adal/xml_namespaces.rb +64 -0
- data/lib/adal.rb +24 -0
- data/samples/authorization_code_example/README.md +10 -0
- data/samples/authorization_code_example/web_app.rb +139 -0
- data/samples/client_assertion_certificate_example/README.md +42 -0
- data/samples/client_assertion_certificate_example/app.rb +55 -0
- data/samples/on_behalf_of_example/README.md +35 -0
- data/samples/on_behalf_of_example/native_app.rb +52 -0
- data/samples/on_behalf_of_example/web_api.rb +71 -0
- data/samples/user_credentials_example/README.md +7 -0
- data/samples/user_credentials_example/app.rb +52 -0
- data/spec/adal/authentication_context_spec.rb +186 -0
- data/spec/adal/authentication_parameters_spec.rb +107 -0
- data/spec/adal/authority_spec.rb +122 -0
- data/spec/adal/cache_driver_spec.rb +191 -0
- data/spec/adal/cached_token_response_spec.rb +148 -0
- data/spec/adal/client_assertion_certificate_spec.rb +113 -0
- data/spec/adal/client_assertion_spec.rb +38 -0
- data/spec/adal/core_ext/hash_spec.rb +47 -0
- data/spec/adal/logging_spec.rb +48 -0
- data/spec/adal/memory_cache_spec.rb +107 -0
- data/spec/adal/mex_request_spec.rb +57 -0
- data/spec/adal/mex_response_spec.rb +143 -0
- data/spec/adal/self_signed_jwt_factory_spec.rb +63 -0
- data/spec/adal/token_request_spec.rb +150 -0
- data/spec/adal/token_response_spec.rb +102 -0
- data/spec/adal/user_credential_spec.rb +125 -0
- data/spec/adal/user_identifier_spec.rb +115 -0
- data/spec/adal/wstrust_request_spec.rb +51 -0
- data/spec/adal/wstrust_response_spec.rb +152 -0
- data/spec/fixtures/mex/insecureaddress.xml +924 -0
- data/spec/fixtures/mex/invalid_namespaces.xml +916 -0
- data/spec/fixtures/mex/malformed.xml +914 -0
- data/spec/fixtures/mex/microsoft.xml +916 -0
- data/spec/fixtures/mex/multiple_endpoints.xml +922 -0
- data/spec/fixtures/mex/no_matching_bindings.xml +916 -0
- data/spec/fixtures/mex/no_username_token_policies.xml +914 -0
- data/spec/fixtures/mex/no_wstrust_endpoints.xml +838 -0
- data/spec/fixtures/mex/only_13.xml +842 -0
- data/spec/fixtures/mex/only_2005.xml +842 -0
- data/spec/fixtures/oauth/error.json +1 -0
- data/spec/fixtures/oauth/success.json +1 -0
- data/spec/fixtures/oauth/success_with_id_token.json +1 -0
- data/spec/fixtures/wstrust/error.xml +24 -0
- data/spec/fixtures/wstrust/invalid_namespaces.xml +136 -0
- data/spec/fixtures/wstrust/missing_security_tokens.xml +90 -0
- data/spec/fixtures/wstrust/success.xml +136 -0
- data/spec/fixtures/wstrust/token.xml +1 -0
- data/spec/fixtures/wstrust/too_many_security_tokens.xml +219 -0
- data/spec/fixtures/wstrust/unrecognized_token_type.xml +136 -0
- data/spec/fixtures/wstrust/wstrust.13.xml +1 -0
- data/spec/fixtures/wstrust/wstrust.2005.xml +89 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/fake_data.rb +40 -0
- data/spec/support/fake_token_endpoint.rb +108 -0
- metadata +264 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
require_relative './logging'
|
|
24
|
+
require_relative './xml_namespaces'
|
|
25
|
+
|
|
26
|
+
require 'nokogiri'
|
|
27
|
+
require 'uri'
|
|
28
|
+
|
|
29
|
+
module ADAL
|
|
30
|
+
# Relevant fields from a Mex response.
|
|
31
|
+
class MexResponse
|
|
32
|
+
include XmlNamespaces
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
include Logging
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class MexError < StandardError; end
|
|
39
|
+
|
|
40
|
+
POLICY_ID_XPATH =
|
|
41
|
+
'//wsdl:definitions/wsp:Policy[./wsp:ExactlyOne/wsp:All/sp:SignedSuppor' \
|
|
42
|
+
'tingTokens/wsp:Policy/sp:UsernameToken/wsp:Policy/sp:WssUsernameToken1' \
|
|
43
|
+
'0]/@u:Id|//wsdl:definitions/wsp:Policy[./wsp:ExactlyOne/wsp:All/ssp:Si' \
|
|
44
|
+
'gnedEncryptedSupportingTokens/wsp:Policy/ssp:UsernameToken/wsp:Policy/' \
|
|
45
|
+
'ssp:WssUsernameToken10]/@u:Id'
|
|
46
|
+
BINDING_XPATH = '//wsdl:definitions/wsdl:binding[./wsp:PolicyReference]'
|
|
47
|
+
PORT_XPATH = '//wsdl:definitions/wsdl:service/wsdl:port'
|
|
48
|
+
ADDRESS_XPATH = './soap12:address/@location'
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Parses the XML string response from the Metadata Exchange endpoint into
|
|
52
|
+
# a MexResponse object.
|
|
53
|
+
#
|
|
54
|
+
# @param String response
|
|
55
|
+
# @return MexResponse
|
|
56
|
+
def self.parse(response)
|
|
57
|
+
xml = Nokogiri::XML(response)
|
|
58
|
+
policy_ids = parse_policy_ids(xml)
|
|
59
|
+
bindings = parse_bindings(xml, policy_ids)
|
|
60
|
+
endpoint, binding = parse_endpoint_and_binding(xml, bindings)
|
|
61
|
+
MexResponse.new(endpoint, binding)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param Nokogiri::XML::Document xml
|
|
65
|
+
# @param Array[String] policy_ids
|
|
66
|
+
# @return Array[String]
|
|
67
|
+
def self.parse_bindings(xml, policy_ids)
|
|
68
|
+
matching_bindings = xml.xpath(BINDING_XPATH, NAMESPACES).map do |node|
|
|
69
|
+
reference_uri = node.xpath('./wsp:PolicyReference/@URI', NAMESPACES)
|
|
70
|
+
node.xpath('./@name').to_s if policy_ids.include? reference_uri.to_s
|
|
71
|
+
end.compact
|
|
72
|
+
fail MexError, 'No matching bindings found.' if matching_bindings.empty?
|
|
73
|
+
matching_bindings
|
|
74
|
+
end
|
|
75
|
+
private_class_method :parse_bindings
|
|
76
|
+
|
|
77
|
+
# @param Nokogiri::XML::Document xml
|
|
78
|
+
# @param Array[String] bindings
|
|
79
|
+
# @return Array[[String, String]]
|
|
80
|
+
def self.parse_all_endpoints(xml, bindings)
|
|
81
|
+
endpoints = xml.xpath(PORT_XPATH, NAMESPACES).map do |node|
|
|
82
|
+
binding = node.attr('binding').split(':').last
|
|
83
|
+
if bindings.include? binding
|
|
84
|
+
[node.xpath(ADDRESS_XPATH, NAMESPACES).to_s, binding]
|
|
85
|
+
end
|
|
86
|
+
end.compact
|
|
87
|
+
endpoints
|
|
88
|
+
end
|
|
89
|
+
private_class_method :parse_all_endpoints
|
|
90
|
+
|
|
91
|
+
# @param Nokogiri::XML::Document xml
|
|
92
|
+
# @param Array[String] bindings
|
|
93
|
+
# @return [String, String]
|
|
94
|
+
def self.parse_endpoint_and_binding(xml, bindings)
|
|
95
|
+
endpoints = parse_all_endpoints(xml, bindings)
|
|
96
|
+
case endpoints.size
|
|
97
|
+
when 0
|
|
98
|
+
fail MexError, 'No valid WS-Trust endpoints found.'
|
|
99
|
+
when 1
|
|
100
|
+
else
|
|
101
|
+
logger.info('Multiple WS-Trust endpoints were found in the mex ' \
|
|
102
|
+
'response. Only one was used.')
|
|
103
|
+
end
|
|
104
|
+
prefer_13(endpoints).first
|
|
105
|
+
end
|
|
106
|
+
private_class_method :parse_endpoint_and_binding
|
|
107
|
+
|
|
108
|
+
# @param Nokogiri::XML::Document xml
|
|
109
|
+
# @return Array[String]
|
|
110
|
+
def self.parse_policy_ids(xml)
|
|
111
|
+
policy_ids = xml.xpath(POLICY_ID_XPATH, NAMESPACES)
|
|
112
|
+
.map { |attr| "\##{attr.value}" }
|
|
113
|
+
fail MexError, 'No username token policy nodes.' if policy_ids.empty?
|
|
114
|
+
policy_ids
|
|
115
|
+
end
|
|
116
|
+
private_class_method :parse_policy_ids
|
|
117
|
+
|
|
118
|
+
# @param Array[String, String] endpoints
|
|
119
|
+
# @return Array[String, String] endpoints
|
|
120
|
+
def self.prefer_13(endpoints)
|
|
121
|
+
only13 = endpoints.select { |_, b| BINDING_TO_ACTION[b] == WSTRUST_13 }
|
|
122
|
+
only13.empty? ? endpoints : only13
|
|
123
|
+
end
|
|
124
|
+
private_class_method :prefer_13
|
|
125
|
+
|
|
126
|
+
attr_reader :action
|
|
127
|
+
attr_reader :wstrust_url
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# Constructs a new MexResponse.
|
|
131
|
+
#
|
|
132
|
+
# @param String|URI wstrust_url
|
|
133
|
+
# @param String action
|
|
134
|
+
def initialize(wstrust_url, binding)
|
|
135
|
+
@action = BINDING_TO_ACTION[binding]
|
|
136
|
+
@wstrust_url = URI.parse(wstrust_url.to_s)
|
|
137
|
+
return if @wstrust_url.instance_of? URI::HTTPS
|
|
138
|
+
fail ArgumentError, 'Mex is only done over HTTPS.'
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
module ADAL
|
|
24
|
+
# A cache implementation that holds no values and ignores all method calls.
|
|
25
|
+
class NoopCache
|
|
26
|
+
# Swallows any number of parameters and returns nil.
|
|
27
|
+
def noop(*); end
|
|
28
|
+
|
|
29
|
+
alias_method :add, :noop
|
|
30
|
+
alias_method :add_many, :noop
|
|
31
|
+
alias_method :remove, :noop
|
|
32
|
+
alias_method :remove_many, :noop
|
|
33
|
+
|
|
34
|
+
def find(*)
|
|
35
|
+
[]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
require_relative './logging'
|
|
24
|
+
require_relative './request_parameters'
|
|
25
|
+
require_relative './util'
|
|
26
|
+
|
|
27
|
+
require 'net/http'
|
|
28
|
+
require 'uri'
|
|
29
|
+
|
|
30
|
+
module ADAL
|
|
31
|
+
# A request that can be made to an authentication or token server.
|
|
32
|
+
class OAuthRequest
|
|
33
|
+
include RequestParameters
|
|
34
|
+
include Util
|
|
35
|
+
|
|
36
|
+
DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'
|
|
37
|
+
DEFAULT_ENCODING = 'utf8'
|
|
38
|
+
SSL_SCHEME = 'https'
|
|
39
|
+
|
|
40
|
+
def initialize(endpoint, params)
|
|
41
|
+
@endpoint_uri = URI.parse(endpoint.to_s)
|
|
42
|
+
@params = params
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def params
|
|
46
|
+
default_parameters.merge(@params)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Requests and waits for a token from the endpoint.
|
|
51
|
+
# @return TokenResponse
|
|
52
|
+
def execute
|
|
53
|
+
request = Net::HTTP::Post.new(@endpoint_uri.path)
|
|
54
|
+
add_headers(request)
|
|
55
|
+
request.body = URI.encode_www_form(string_hash(params))
|
|
56
|
+
TokenResponse.parse(http(@endpoint_uri).request(request).body)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Adds the necessary OAuth headers.
|
|
63
|
+
#
|
|
64
|
+
# @param Net::HTTPGenericRequest
|
|
65
|
+
def add_headers(request)
|
|
66
|
+
return if Logging.correlation_id.nil?
|
|
67
|
+
request.add_field(CLIENT_REQUEST_ID.to_s, Logging.correlation_id)
|
|
68
|
+
request.add_field(CLIENT_RETURN_CLIENT_REQUEST_ID.to_s, true)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def default_parameters
|
|
72
|
+
{ encoding: DEFAULT_ENCODING,
|
|
73
|
+
AAD_API_VERSION => '1.0' }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
module ADAL
|
|
24
|
+
# Names of parameters in OAuth requests. This module can be included in any
|
|
25
|
+
# class to reference the parameters instead of referring to them as strings
|
|
26
|
+
# or symbols
|
|
27
|
+
module RequestParameters
|
|
28
|
+
AAD_API_VERSION = 'api-version'.to_sym
|
|
29
|
+
ASSERTION = 'assertion'.to_sym
|
|
30
|
+
CLIENT_ASSERTION = 'client_assertion'.to_sym
|
|
31
|
+
CLIENT_ASSERTION_TYPE = 'client_assertion_type'.to_sym
|
|
32
|
+
CLIENT_ID = 'client_id'.to_sym
|
|
33
|
+
CLIENT_REQUEST_ID = 'client-request-id'.to_sym
|
|
34
|
+
CLIENT_RETURN_CLIENT_REQUEST_ID = 'client-return-client-request-id'.to_sym
|
|
35
|
+
CLIENT_SECRET = 'client_secret'.to_sym
|
|
36
|
+
CODE = 'code'.to_sym
|
|
37
|
+
FORM_POST = 'form_post'.to_sym
|
|
38
|
+
GRANT_TYPE = 'grant_type'.to_sym
|
|
39
|
+
PASSWORD = 'password'.to_sym
|
|
40
|
+
REDIRECT_URI = 'redirect_uri'.to_sym
|
|
41
|
+
REFRESH_TOKEN = 'refresh_token'.to_sym
|
|
42
|
+
RESOURCE = 'resource'.to_sym
|
|
43
|
+
SCOPE = 'scope'.to_sym
|
|
44
|
+
UNIQUE_ID = 'unique_id'.to_sym
|
|
45
|
+
USER_INFO = 'user_info'.to_sym
|
|
46
|
+
USERNAME = 'username'.to_sym
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
require_relative './jwt_parameters'
|
|
24
|
+
require_relative './logging'
|
|
25
|
+
|
|
26
|
+
require 'jwt'
|
|
27
|
+
require 'openssl'
|
|
28
|
+
require 'securerandom'
|
|
29
|
+
|
|
30
|
+
module ADAL
|
|
31
|
+
# Converts client certificates into self signed JWTs.
|
|
32
|
+
class SelfSignedJwtFactory
|
|
33
|
+
include JwtParameters
|
|
34
|
+
include Logging
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Constructs a new SelfSignedJwtFactory.
|
|
38
|
+
#
|
|
39
|
+
# @param String client_id
|
|
40
|
+
# The client id of the calling application.
|
|
41
|
+
# @param String token_endpoint
|
|
42
|
+
# The token endpoint that will accept the certificate.
|
|
43
|
+
def initialize(client_id, token_endpoint)
|
|
44
|
+
@client_id = client_id
|
|
45
|
+
@token_endpoint = token_endpoint
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Creates a JWT from a client certificate and signs it with a private key.
|
|
50
|
+
#
|
|
51
|
+
# @param OpenSSL::X509::Certificate certificate
|
|
52
|
+
# The certifcate object to be converted to a JWT and signed for use
|
|
53
|
+
# in an authentication flow.
|
|
54
|
+
# @param OpenSSL::PKey::RSA private_key
|
|
55
|
+
# The private key used to sign the certificate.
|
|
56
|
+
# @return String
|
|
57
|
+
def create_and_sign_jwt(certificate, private_key)
|
|
58
|
+
JWT.encode(payload, private_key, RS256, header(certificate))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# The JWT header for a certificate to be encoded.
|
|
64
|
+
def header(certificate)
|
|
65
|
+
x5t = thumbprint(certificate)
|
|
66
|
+
logger.verbose("Creating self signed JWT header with thumbprint: #{x5t}.")
|
|
67
|
+
{ TYPE => TYPE_JWT,
|
|
68
|
+
ALGORITHM => RS256,
|
|
69
|
+
THUMBPRINT => x5t }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The JWT payload.
|
|
73
|
+
def payload
|
|
74
|
+
now = Time.now - 1
|
|
75
|
+
expires = now + 60 * SELF_SIGNED_JWT_LIFETIME
|
|
76
|
+
logger.verbose("Creating self signed JWT payload. Expires: #{expires}. " \
|
|
77
|
+
"NotBefore: #{now}.")
|
|
78
|
+
{ AUDIENCE => @token_endpoint,
|
|
79
|
+
ISSUER => @client_id,
|
|
80
|
+
SUBJECT => @client_id,
|
|
81
|
+
NOT_BEFORE => now.to_i,
|
|
82
|
+
EXPIRES_ON => expires.to_i,
|
|
83
|
+
JWT_ID => SecureRandom.uuid }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Base 64 encoded thumbprint AKA fingerprint AKA SHA1 hash of the
|
|
88
|
+
# DER representation of the cert.
|
|
89
|
+
#
|
|
90
|
+
# @param OpenSSL::X509::Certificate certificate
|
|
91
|
+
# @return String
|
|
92
|
+
def thumbprint(certificate)
|
|
93
|
+
OpenSSL::Digest::SHA1.new(certificate.to_der).base64digest
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
|
|
3
|
+
<s:Header>
|
|
4
|
+
<a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
|
|
5
|
+
<a:messageID>urn:uuid:<%= message_id %></a:messageID>
|
|
6
|
+
<a:ReplyTo>
|
|
7
|
+
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
|
|
8
|
+
</a:ReplyTo>
|
|
9
|
+
<a:To s:mustUnderstand="1"><%= @endpoint %></a:To>
|
|
10
|
+
<o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
|
|
11
|
+
<u:Timestamp u:Id="_0">
|
|
12
|
+
<u:Created><%= created.utc.iso8601 %></u:Created>
|
|
13
|
+
<u:Expires><%= expires.utc.iso8601 %></u:Expires>
|
|
14
|
+
</u:Timestamp>
|
|
15
|
+
<o:UsernameToken u:Id="ADALUsernameToken">
|
|
16
|
+
<o:Username><%= username.encode(xml: :text) %></o:Username>
|
|
17
|
+
<o:Password><%= password.encode(xml: :text) %></o:Password>
|
|
18
|
+
</o:UsernameToken>
|
|
19
|
+
</o:Security>
|
|
20
|
+
</s:Header>
|
|
21
|
+
<s:Body>
|
|
22
|
+
<trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
|
|
23
|
+
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
|
|
24
|
+
<a:EndpointReference>
|
|
25
|
+
<a:Address><%= @applies_to %></a:Address>
|
|
26
|
+
</a:EndpointReference>
|
|
27
|
+
</wsp:AppliesTo>
|
|
28
|
+
<trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
|
|
29
|
+
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
|
|
30
|
+
</trust:RequestSecurityToken>
|
|
31
|
+
</s:Body>
|
|
32
|
+
</s:Envelope>
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
|
|
3
|
+
<s:Header>
|
|
4
|
+
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
|
|
5
|
+
<a:messageID>urn:uuid:<%= message_id %></a:messageID>
|
|
6
|
+
<a:ReplyTo>
|
|
7
|
+
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
|
|
8
|
+
</a:ReplyTo>
|
|
9
|
+
<a:To s:mustUnderstand="1"><%= @endpoint %></a:To>
|
|
10
|
+
<o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
|
|
11
|
+
<u:Timestamp u:Id="_0">
|
|
12
|
+
<u:Created><%= created.utc.iso8601 %></u:Created>
|
|
13
|
+
<u:Expires><%= expires.utc.iso8601 %></u:Expires>
|
|
14
|
+
</u:Timestamp>
|
|
15
|
+
<o:UsernameToken u:Id="ADALUsernameToken">
|
|
16
|
+
<o:Username><%= username.encode(xml: :text) %></o:Username>
|
|
17
|
+
<o:Password><%= password.encode(xml: :text) %></o:Password>
|
|
18
|
+
</o:UsernameToken>
|
|
19
|
+
</o:Security>
|
|
20
|
+
</s:Header>
|
|
21
|
+
<s:Body>
|
|
22
|
+
<trust:RequestSecurityToken xmlns:trust="http://schemas.xmlsoap.org/ws/2005/02/trust">
|
|
23
|
+
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
|
|
24
|
+
<a:EndpointReference>
|
|
25
|
+
<a:Address><%= @applies_to %></a:Address>
|
|
26
|
+
</a:EndpointReference>
|
|
27
|
+
</wsp:AppliesTo>
|
|
28
|
+
<trust:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</trust:KeyType>
|
|
29
|
+
<trust:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</trust:RequestType>
|
|
30
|
+
</trust:RequestSecurityToken>
|
|
31
|
+
</s:Body>
|
|
32
|
+
</s:Envelope>
|