adal 1.0.0
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 +5 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +97 -0
- data/Rakefile +39 -0
- data/adal.gemspec +52 -0
- data/contributing.md +127 -0
- data/lib/adal.rb +24 -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.rb +26 -0
- data/lib/adal/core_ext/hash.rb +34 -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/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 +265 -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,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
|