adal 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|