custom-adal 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rubocop.yml +7 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +25 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +106 -0
  8. data/Rakefile +39 -0
  9. data/adal.gemspec +52 -0
  10. data/contributing.md +127 -0
  11. data/lib/adal/authentication_context.rb +202 -0
  12. data/lib/adal/authentication_parameters.rb +126 -0
  13. data/lib/adal/authority.rb +165 -0
  14. data/lib/adal/cache_driver.rb +171 -0
  15. data/lib/adal/cached_token_response.rb +190 -0
  16. data/lib/adal/client_assertion.rb +63 -0
  17. data/lib/adal/client_assertion_certificate.rb +89 -0
  18. data/lib/adal/client_credential.rb +46 -0
  19. data/lib/adal/core_ext/hash.rb +34 -0
  20. data/lib/adal/core_ext.rb +26 -0
  21. data/lib/adal/jwt_parameters.rb +39 -0
  22. data/lib/adal/logger.rb +90 -0
  23. data/lib/adal/logging.rb +98 -0
  24. data/lib/adal/memory_cache.rb +95 -0
  25. data/lib/adal/mex_request.rb +52 -0
  26. data/lib/adal/mex_response.rb +141 -0
  27. data/lib/adal/noop_cache.rb +38 -0
  28. data/lib/adal/oauth_request.rb +76 -0
  29. data/lib/adal/request_parameters.rb +48 -0
  30. data/lib/adal/self_signed_jwt_factory.rb +96 -0
  31. data/lib/adal/templates/rst.13.xml.erb +35 -0
  32. data/lib/adal/templates/rst.2005.xml.erb +32 -0
  33. data/lib/adal/token_request.rb +231 -0
  34. data/lib/adal/token_response.rb +144 -0
  35. data/lib/adal/user_assertion.rb +57 -0
  36. data/lib/adal/user_credential.rb +152 -0
  37. data/lib/adal/user_identifier.rb +83 -0
  38. data/lib/adal/user_information.rb +49 -0
  39. data/lib/adal/util.rb +49 -0
  40. data/lib/adal/version.rb +36 -0
  41. data/lib/adal/wstrust_request.rb +100 -0
  42. data/lib/adal/wstrust_response.rb +168 -0
  43. data/lib/adal/xml_namespaces.rb +64 -0
  44. data/lib/adal.rb +24 -0
  45. data/samples/authorization_code_example/README.md +10 -0
  46. data/samples/authorization_code_example/web_app.rb +139 -0
  47. data/samples/client_assertion_certificate_example/README.md +42 -0
  48. data/samples/client_assertion_certificate_example/app.rb +55 -0
  49. data/samples/on_behalf_of_example/README.md +35 -0
  50. data/samples/on_behalf_of_example/native_app.rb +52 -0
  51. data/samples/on_behalf_of_example/web_api.rb +71 -0
  52. data/samples/user_credentials_example/README.md +7 -0
  53. data/samples/user_credentials_example/app.rb +52 -0
  54. data/spec/adal/authentication_context_spec.rb +186 -0
  55. data/spec/adal/authentication_parameters_spec.rb +107 -0
  56. data/spec/adal/authority_spec.rb +122 -0
  57. data/spec/adal/cache_driver_spec.rb +191 -0
  58. data/spec/adal/cached_token_response_spec.rb +148 -0
  59. data/spec/adal/client_assertion_certificate_spec.rb +113 -0
  60. data/spec/adal/client_assertion_spec.rb +38 -0
  61. data/spec/adal/core_ext/hash_spec.rb +47 -0
  62. data/spec/adal/logging_spec.rb +48 -0
  63. data/spec/adal/memory_cache_spec.rb +107 -0
  64. data/spec/adal/mex_request_spec.rb +57 -0
  65. data/spec/adal/mex_response_spec.rb +143 -0
  66. data/spec/adal/self_signed_jwt_factory_spec.rb +63 -0
  67. data/spec/adal/token_request_spec.rb +150 -0
  68. data/spec/adal/token_response_spec.rb +102 -0
  69. data/spec/adal/user_credential_spec.rb +125 -0
  70. data/spec/adal/user_identifier_spec.rb +115 -0
  71. data/spec/adal/wstrust_request_spec.rb +51 -0
  72. data/spec/adal/wstrust_response_spec.rb +152 -0
  73. data/spec/fixtures/mex/insecureaddress.xml +924 -0
  74. data/spec/fixtures/mex/invalid_namespaces.xml +916 -0
  75. data/spec/fixtures/mex/malformed.xml +914 -0
  76. data/spec/fixtures/mex/microsoft.xml +916 -0
  77. data/spec/fixtures/mex/multiple_endpoints.xml +922 -0
  78. data/spec/fixtures/mex/no_matching_bindings.xml +916 -0
  79. data/spec/fixtures/mex/no_username_token_policies.xml +914 -0
  80. data/spec/fixtures/mex/no_wstrust_endpoints.xml +838 -0
  81. data/spec/fixtures/mex/only_13.xml +842 -0
  82. data/spec/fixtures/mex/only_2005.xml +842 -0
  83. data/spec/fixtures/oauth/error.json +1 -0
  84. data/spec/fixtures/oauth/success.json +1 -0
  85. data/spec/fixtures/oauth/success_with_id_token.json +1 -0
  86. data/spec/fixtures/wstrust/error.xml +24 -0
  87. data/spec/fixtures/wstrust/invalid_namespaces.xml +136 -0
  88. data/spec/fixtures/wstrust/missing_security_tokens.xml +90 -0
  89. data/spec/fixtures/wstrust/success.xml +136 -0
  90. data/spec/fixtures/wstrust/token.xml +1 -0
  91. data/spec/fixtures/wstrust/too_many_security_tokens.xml +219 -0
  92. data/spec/fixtures/wstrust/unrecognized_token_type.xml +136 -0
  93. data/spec/fixtures/wstrust/wstrust.13.xml +1 -0
  94. data/spec/fixtures/wstrust/wstrust.2005.xml +89 -0
  95. data/spec/spec_helper.rb +53 -0
  96. data/spec/support/fake_data.rb +40 -0
  97. data/spec/support/fake_token_endpoint.rb +108 -0
  98. metadata +264 -0
@@ -0,0 +1,42 @@
1
+ ## Authenticating with a Certificate and Private Key
2
+
3
+ ### Set-Up
4
+ 1. Register a web application in the Azure portal. Note it's client id.
5
+ 2. Generate a self-issued certificate with key length >= 2048. Using the Windows makecert.exe utility, this can be done with:
6
+
7
+ ```
8
+ makecert -r -pe -n "CN=Name of Certificate" -b 07/01/2015 -e 07/01/2017 -ss my -len 2048
9
+ ```
10
+ 3. Export the certificate from Certificate Manager to a file.
11
+ 4. Get the base 64 thumbprint, base 64 value and key id from the certificate. With Windows powershell, this can be done with:
12
+
13
+ ```
14
+ $cer = New-Object System.Security.Cryptograph.X509Certificates.X509Certificate2
15
+ $cer.Import(".\path\to\cert\from\step3.cer")
16
+ $base64thumbprint = [System.Convert]::ToBase64String($cer.GetRawCertData())
17
+ $base64value = [System.Convert]::ToBase64String($cer.GetRawCertData())
18
+ $keyId = [System.Guid]::NewGuid().ToString()
19
+ ```
20
+ 5. Download the manifest for the registered web application, add the following entry to the keyCredentials array and reupload it:
21
+
22
+ ```
23
+ {
24
+ "customKeyIdentifier": "[base 64 thumbprint]",
25
+ "keyId": "[key id]",
26
+ "type": "AsymmetricX509Cert",
27
+ "usage": "Verify",
28
+ "value": "[base 64 value]"
29
+ }
30
+ ```
31
+ 6. Export the certificate as a PFX (PKCS12). This can be done via the Windows Certificate Manager GUI.
32
+ 7. Fill in your tenant, client id of the web application and path and password to your .pfx file in app.rb.
33
+
34
+ ### Run
35
+ Run the app as ```ruby app.rb```.
36
+
37
+ ## Common problems
38
+ ### I get an error that looks like:
39
+ ```
40
+ ...net/http.rb:xxx: in connect SSL_connect returned=1 errno=0 state=SSLv3 read server certificate verify failed (OpenSSL::SSL::SSLError)
41
+ ```
42
+ This is likely because you are on a Windows system and installed a Ruby installation that ships OpenSSL with no certificate authorities. The most common offender is RailsInstaller. [Solution](https://gist.github.com/fnichol/867550).
@@ -0,0 +1,55 @@
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
+ # See the accompanying README.md for instructions on how to set-up an
24
+ # application to run this sample.
25
+
26
+ require 'adal'
27
+ require 'openssl'
28
+
29
+ # This will make ADAL log the various steps of JWT creation from certificates.
30
+ ADAL::Logging.log_level = ADAL::Logger::VERBOSE
31
+
32
+ AUTHORITY_HOST = ADAL::Authority::WORLD_WIDE_AUTHORITY
33
+ CLIENT_ID = 'your client id here'
34
+ RESOURCE = 'https://outlook.office365.com'
35
+ TENANT = 'your tenant here.onmicrosoft.com'
36
+
37
+ PFX_PATH = './path/to/your/cert.pfx'
38
+ PFX_PASSWORD = 'password'
39
+
40
+ pfx = OpenSSL::PKCS12.new(File.read(PFX_PATH), PFX_PASSWORD)
41
+
42
+ authority = ADAL::Authority.new(AUTHORITY_HOST, TENANT)
43
+ client_cred = ADAL::ClientAssertionCertificate.new(authority, CLIENT_ID, pfx)
44
+ result = ADAL::AuthenticationContext
45
+ .new(AUTHORITY_HOST, TENANT)
46
+ .acquire_token_for_client(RESOURCE, client_cred)
47
+
48
+ case result
49
+ when ADAL::SuccessResponse
50
+ puts 'Successfully authenticated with client credentials. Received access ' \
51
+ "token: #{result.access_token}."
52
+ when ADAL::FailureResponse
53
+ puts 'Failed to authenticate with client credentials. Received error: ' \
54
+ "#{result.error} and error description: #{result.error_description}."
55
+ end
@@ -0,0 +1,35 @@
1
+ # Authenticating On Behalf of a User
2
+
3
+ This sample consists of two applications that each use ADAL.
4
+
5
+ The native application uses a username and password flow to obtain an access
6
+ token and then uses that access token to access a resource: the web api.
7
+
8
+ The web api takes the access token and exchanges it for an access token for
9
+ the graph.windows.net resource and uses it to retrieve graph data, which it
10
+ then sends back to the native app.
11
+
12
+ Before running these applications, follow the instructions below to configure
13
+ them to your tenant.
14
+
15
+
16
+ ## Configuring the Native Application
17
+ 1. In the Azure portal, register a new Web Application. Take note of the client
18
+ id and client secret. The application identifier can be anything, but take
19
+ note of what you choose for a future step.
20
+ 2. Give this application permission to use the web application that you create
21
+ in the next step. (You can't do this step until you've configured the Web
22
+ Application.)
23
+ 3. In `web_app.rb`, fill in the tenant, client id and client secret fields.
24
+
25
+ ## Configuring the Web Application
26
+ 1. In the Azure portal, register a new Native Application. Take note of the
27
+ client id.
28
+ 2. Give this application permission to `Read directory data.`.
29
+ 3. In `native_app.rb`, fill in the tenant and client id field. Fiell in the
30
+ `WEB_API_RESOURCE` field with the application identifier that you used
31
+ previously.
32
+
33
+ ## Running the application
34
+ 1. Start the web application with `ruby web_api.rb`.
35
+ 2. Run the native application with `ruby native_app.rb`.
@@ -0,0 +1,52 @@
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 'adal'
24
+ require 'json'
25
+
26
+ def prompt(*args)
27
+ print(*args)
28
+ gets.strip
29
+ end
30
+
31
+ # Uncomment this if you want to trace ADAL's execution.
32
+ # ADAL::Logging.log_level = ADAL::Logger::VERBOSE
33
+
34
+ AUTHORITY_HOST = ADAL::Authority::WORLD_WIDE_AUTHORITY
35
+ TENANT = 'your tenant here.onmicrosoft.com'
36
+ CLIENT_ID = 'your client id here'
37
+ WEB_API_RESOURCE = 'https://your tenant here.onmicrosoft.com/MyWebService'
38
+ WEB_API_ENDPOINT = 'http://localhost:44321/api/graph'
39
+
40
+ user_cred = ADAL::UserCredential.new(prompt('Username: '), prompt('Password: '))
41
+ ctx = ADAL::AuthenticationContext.new(AUTHORITY_HOST, TENANT)
42
+
43
+ token_response =
44
+ ctx.acquire_token_for_user(WEB_API_RESOURCE, CLIENT_ID, user_cred)
45
+
46
+ web_api_uri = URI(WEB_API_ENDPOINT)
47
+ headers = { 'Bearer' => token_response.access_token }
48
+ http = Net::HTTP.new(web_api_uri.hostname, web_api_uri.port)
49
+ web_api_response = http.get(web_api_uri, headers)
50
+
51
+ puts 'Here is your directory user graph:'
52
+ puts JSON.pretty_generate(JSON.parse(web_api_response.body))
@@ -0,0 +1,71 @@
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 '../../lib/adal'
24
+ require 'sinatra'
25
+
26
+ # Uncomment this if you want to trace ADAL's execution.
27
+ # ADAL::Logging.log_level = ADAL::Logger::VERBOSE
28
+
29
+ AUTHORITY_HOST = ADAL::Authority::WORLD_WIDE_AUTHORITY
30
+ TENANT = 'your tenant here.onmicrosoft.com'
31
+ RESOURCE = 'https://graph.windows.net'
32
+ CLIENT_ID = 'your client id here'
33
+ CLIENT_SECRET = 'your client secret here'
34
+
35
+ set :client_cred, ADAL::ClientCredential.new(CLIENT_ID, CLIENT_SECRET)
36
+ set :ctx, ADAL::AuthenticationContext.new(AUTHORITY_HOST, TENANT)
37
+ set :port, 44_321
38
+
39
+ before do
40
+ # If the client does not send an access token, then he is unauthorized.
41
+ halt 401 unless env['HTTP_BEARER']
42
+
43
+ token = exchange_tokens(env['HTTP_BEARER'])
44
+ env[:access_token] = token.access_token
45
+
46
+ # If we cannot exchange the clients access token for a new one, then he is
47
+ # unauthorized.
48
+ halt 401 unless env[:access_token]
49
+ end
50
+
51
+ # Fetches the contents of the /users graph endpoint.
52
+ get '/api/graph' do
53
+ graph_uri = URI(RESOURCE + '/' + TENANT + '/users?api-version=2013-04-05')
54
+ headers = { 'authorization' => env[:access_token] }
55
+ http = Net::HTTP.new(graph_uri.hostname, graph_uri.port)
56
+ http.use_ssl = true
57
+ http.get(graph_uri, headers).body
58
+ end
59
+
60
+ ##
61
+ # Exchanges an access token for this web api for an access token for another
62
+ # resource.
63
+ #
64
+ # @param String access_token
65
+ # The token for this web service, from the client.
66
+ # @return String
67
+ # An access token for the designated resource.
68
+ def exchange_tokens(access_token)
69
+ settings.ctx.acquire_token_for_user(
70
+ RESOURCE, settings.client_cred, ADAL::UserAssertion.new(access_token))
71
+ end
@@ -0,0 +1,7 @@
1
+ This simple application demonstrates how to use ADAL to acquire access tokens
2
+ with the username/password flow.
3
+
4
+ To run the application:
5
+
6
+ 1. Replace the `CLIENT_ID` and `TENANT` fields with your client id and tenant.
7
+ 2. Run `ruby app.rb`.
@@ -0,0 +1,52 @@
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 'adal'
24
+
25
+ # This will make ADAL log the various steps of obtaining an access token.
26
+ ADAL::Logging.log_level = ADAL::Logger::VERBOSE
27
+
28
+ AUTHORITY_HOST = ADAL::Authority::WORLD_WIDE_AUTHORITY
29
+ CLIENT_ID = 'your clientid here'
30
+ RESOURCE = 'https://graph.windows.net'
31
+ TENANT = 'your tenant here.onmicrosoft.com'
32
+
33
+ def prompt(*args)
34
+ print(*args)
35
+ gets.strip
36
+ end
37
+
38
+ username = prompt 'Username: '
39
+ password = prompt 'Password: '
40
+
41
+ user_cred = ADAL::UserCredential.new(username, password)
42
+ ctx = ADAL::AuthenticationContext.new(AUTHORITY_HOST, TENANT)
43
+ result = ctx.acquire_token_for_user(RESOURCE, CLIENT_ID, user_cred)
44
+
45
+ case result
46
+ when ADAL::SuccessResponse
47
+ puts 'Successfully authenticated with user credentials. Received access ' \
48
+ "token: #{result.access_token}."
49
+ when ADAL::FailureResponse
50
+ puts 'Failed to authenticate with client credentials. Received error: ' \
51
+ "#{result.error} and error description: #{result.error_description}."
52
+ end
@@ -0,0 +1,186 @@
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 '../support/fake_data'
24
+
25
+ require 'spec_helper'
26
+
27
+ # Load constants used by the fake token endpoint.
28
+ include FakeData
29
+
30
+ describe ADAL::AuthenticationContext do
31
+ let(:auth_ctx) { ADAL::AuthenticationContext.new(AUTHORITY, TENANT) }
32
+ let(:client_cred) { ADAL::ClientCredential.new(CLIENT_ID, CLIENT_SECRET) }
33
+
34
+ describe '#authorization_request_url' do
35
+ let(:resource) { 'http://graph.windows.net' }
36
+ let(:client_id) { 'some-client-id' }
37
+ let(:redirect_uri) { 'http://contoso.com/login' }
38
+
39
+ context 'with no extra params' do
40
+ it 'should produce the correct request url' do
41
+ authorization_url =
42
+ auth_ctx.authorization_request_url(resource, client_id, redirect_uri)
43
+ expect(authorization_url.to_s.strip)
44
+ .to eq "https://#{AUTHORITY}/#{TENANT}/oauth2/authorize?client_id=" \
45
+ "#{client_id}&response_mode=form_post&redirect_uri=http%3A%2" \
46
+ 'F%2Fcontoso.com%2Flogin&resource=http%3A%2F%2Fgraph.windows' \
47
+ '.net&response_type=code'
48
+ end
49
+ end
50
+
51
+ context 'with extra params' do
52
+ it 'should produce the correct request url' do
53
+ params = { foo: :bar }
54
+ authorization_url = auth_ctx.authorization_request_url(
55
+ resource, client_id, redirect_uri, params)
56
+ expect(authorization_url.to_s.strip)
57
+ .to eq "https://#{AUTHORITY}/#{TENANT}/oauth2/authorize?client_id=" \
58
+ "#{client_id}&response_mode=form_post&redirect_uri=http%3A%2" \
59
+ 'F%2Fcontoso.com%2Flogin&resource=http%3A%2F%2Fgraph.windows' \
60
+ '.net&response_type=code&foo=bar'
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#correlation_id=' do
66
+ let(:correlation_id) { 'correlation_id_1' }
67
+ let(:user) { ADAL::UserAssertion.new(USER_ASSERTION) }
68
+
69
+ it 'should put the correlation id on all request headers' do
70
+ auth_ctx.correlation_id = correlation_id
71
+ stub_request(:post, %r{.*#{TENANT}\/oauth2\/token})
72
+ .with(headers: { 'client-request-id' => correlation_id })
73
+ .and_return(body: '{"access_token":"my access token"}')
74
+ result = auth_ctx.acquire_token_for_user(RESOURCE, CLIENT_ID, user)
75
+ expect(result.access_token).to eq('my access token')
76
+ end
77
+ end
78
+
79
+ describe '#acquire_token_with_authorization_code' do
80
+ it 'should return a SuccessResponse when successful' do
81
+ token_response = auth_ctx.acquire_token_with_authorization_code(
82
+ AUTH_CODE, REDIRECT_URI, client_cred, RESOURCE)
83
+ expect(token_response).to be_a(ADAL::SuccessResponse)
84
+ end
85
+
86
+ it 'should return an ErrorResponse when unauthorized' do
87
+ token_response = auth_ctx.acquire_token_with_authorization_code(
88
+ AUTH_CODE, 'bad', client_cred, RESOURCE)
89
+ expect(token_response).to be_a(ADAL::ErrorResponse)
90
+ end
91
+
92
+ it 'should fail if any of the required parameters are nil' do
93
+ expect do
94
+ auth_ctx.acquire_token_with_authorization_code(
95
+ nil, REDIRECT_URI, client_cred, RESOURCE)
96
+ end.to raise_error(ArgumentError)
97
+ end
98
+ end
99
+
100
+ describe '#acquire_token_for_client' do
101
+ it 'should return a SuccessResponse when successful' do
102
+ response = auth_ctx.acquire_token_for_client(RESOURCE, client_cred)
103
+ expect(response).to be_a(ADAL::SuccessResponse)
104
+ end
105
+
106
+ it 'should return an ErrorResponse when unauthorized' do
107
+ token_response = auth_ctx.acquire_token_for_client(RESOURCE, 'bad')
108
+ expect(token_response).to be_a(ADAL::ErrorResponse)
109
+ end
110
+
111
+ it 'should fail if any of the parameters are nil' do
112
+ expect do
113
+ auth_ctx.acquire_token_for_client(RESOURCE, nil)
114
+ end.to raise_error(ArgumentError)
115
+ end
116
+ end
117
+
118
+ describe '#acquire_token_with_refresh_token' do
119
+ it 'should return a SuccessResponse when successful' do
120
+ token_response = auth_ctx.acquire_token_with_refresh_token(
121
+ REFRESH_TOKEN, client_cred, RESOURCE)
122
+ expect(token_response).to be_a(ADAL::SuccessResponse)
123
+ end
124
+
125
+ it 'should return an ErrorResponse when unauthorized' do
126
+ token_response = auth_ctx.acquire_token_with_refresh_token(
127
+ REFRESH_TOKEN, 'bad', RESOURCE)
128
+ expect(token_response).to be_a(ADAL::ErrorResponse)
129
+ end
130
+
131
+ it 'should return an ErrorResponse when the refresh token is invalid' do
132
+ token_response = auth_ctx.acquire_token_with_refresh_token(
133
+ 'bad', client_cred, RESOURCE)
134
+ expect(token_response).to be_a(ADAL::ErrorResponse)
135
+ end
136
+
137
+ it 'should fail if any of the required parameters are nil' do
138
+ expect do
139
+ auth_ctx.acquire_token_with_refresh_token(
140
+ nil, client_cred, RESOURCE)
141
+ end.to raise_error(ArgumentError)
142
+ end
143
+ end
144
+
145
+ describe '#acquire_token_for_user' do
146
+ context 'with valid parameters' do
147
+ let(:user) { ADAL::UserAssertion.new(USER_ASSERTION) }
148
+ subject { auth_ctx.acquire_token_for_user(RESOURCE, client_cred, user) }
149
+
150
+ it { is_expected.to be_a(ADAL::SuccessResponse) }
151
+ end
152
+
153
+ context 'with invalid parameters' do
154
+ it 'should fail' do
155
+ expect { auth_ctx.acquire_token_for_user(nil, nil) }
156
+ .to raise_error(ArgumentError)
157
+ end
158
+ end
159
+
160
+ context 'with a UserIdentifier' do
161
+ let(:user) { ADAL::UserIdentifier.new(USERNAME, :DISPLAYABLE_ID) }
162
+ context 'with a matching token in the cache' do
163
+ subject { auth_ctx.acquire_token_for_user(RESOURCE, client_cred, user) }
164
+ before(:each) do
165
+ @first_response = auth_ctx.acquire_token_with_authorization_code(
166
+ AUTH_CODE, REDIRECT_URI, client_cred, RESOURCE)
167
+ end
168
+
169
+ it { is_expected.to_not be nil }
170
+
171
+ it 'should return the token from the cache' do
172
+ expect(subject).to eq @first_response
173
+ end
174
+ end
175
+
176
+ context 'with no matching token in the cache' do
177
+ subject do
178
+ -> { auth_ctx.acquire_token_for_user(RESOURCE, client_cred, user) }
179
+ end
180
+ it do
181
+ is_expected.to raise_error ADAL::TokenRequest::UserCredentialError
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,107 @@
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 '../spec_helper'
24
+
25
+ require 'uri'
26
+
27
+ describe ADAL::AuthenticationParameters do
28
+ describe '::create_from_resource_url' do
29
+ it 'should make an request to the resource server and parse the response' do
30
+ expect(Net::HTTP).to receive(:post_form).once.and_return(
31
+ 'www-authenticate' => 'Bearer authorization_uri="sample.com"')
32
+ params = ADAL::AuthenticationParameters.create_from_resource_url('a.io')
33
+ expect(params.authority_uri).to eq(URI.parse('sample.com'))
34
+ end
35
+
36
+ it 'should fail if the url is not a valid url' do
37
+ bad_url = 'this is not a valid url'
38
+ expect do
39
+ ADAL::AuthenticationParameters.create_from_resource_url(bad_url)
40
+ end.to raise_error(URI::InvalidURIError)
41
+ end
42
+
43
+ it 'should fail if the response does not contain an authenticate header' do
44
+ allow(Net::HTTP).to receive(:post_form).and_return(
45
+ body: 'abc', 'content-type' => 'application/json')
46
+ expect do
47
+ ADAL::AuthenticationParameters.create_from_resource_url('myurl.com')
48
+ end.to raise_error(ArgumentError)
49
+ end
50
+
51
+ it 'should fail if the resource server does not response' do
52
+ allow(Net::HTTP).to receive(:post_form).and_raise(Timeout::Error)
53
+ expect do
54
+ ADAL::AuthenticationParameters.create_from_resource_url('myurl.com')
55
+ end.to raise_error(Timeout::Error)
56
+ end
57
+ end
58
+
59
+ describe '::create_from_authenticate_header' do
60
+ it 'should successfully parse a valid authentication header' do
61
+ params = ADAL::AuthenticationParameters.create_from_authenticate_header(
62
+ 'Bearer authorization_uri="foobar,lj,;l,", resource="a( res&*^ource"')
63
+ expect(params.authority_uri).to eq(URI.parse('foobar,lj,;l,'))
64
+ expect(params.resource).to eq('a( res&*^ource')
65
+ end
66
+
67
+ it 'should return nil if the input is nil' do
68
+ expect(
69
+ ADAL::AuthenticationParameters.create_from_authenticate_header(nil)
70
+ ).to be_nil
71
+ end
72
+
73
+ it 'should return nil if the header does not contain an authority key' do
74
+ expect(
75
+ ADAL::AuthenticationParameters.create_from_authenticate_header(
76
+ 'Bearer: resource="http://graph.windows.net"')
77
+ ).to be_nil
78
+ end
79
+ end
80
+
81
+ describe '#create_context' do
82
+ before(:each) do
83
+ auth_uri = 'https://login.windows.net/mytenant.onmicrosoft.com'
84
+ @auth_params = ADAL::AuthenticationParameters.new(auth_uri)
85
+ end
86
+
87
+ it 'should return an AuthenticationContext object' do
88
+ expect(@auth_params.create_context).to be_a(ADAL::AuthenticationContext)
89
+ end
90
+
91
+ it 'should extract the host and tenant from the authorize uri' do
92
+ # @authority does not have a getter, so we have to use Ruby voodoo.
93
+ authority = @auth_params.create_context.instance_variable_get(:@authority)
94
+ expect(authority.host).to eq('login.windows.net')
95
+ expect(authority.tenant).to eq('mytenant.onmicrosoft.com')
96
+ end
97
+ end
98
+
99
+ describe '#initialize' do
100
+ it 'should fail if the authorize uri is not a valid uri' do
101
+ bad_auth_uri = 'not a real parsable uri'
102
+ expect do
103
+ ADAL::AuthenticationParameters.new(bad_auth_uri)
104
+ end.to raise_error(URI::InvalidURIError)
105
+ end
106
+ end
107
+ end