Skip to content
This repository has been archived by the owner on Sep 29, 2023. It is now read-only.

Commit

Permalink
Merge pull request #96 from AzureAD/release-0.4.6
Browse files Browse the repository at this point in the history
ADAL Python 0.4.6
  • Loading branch information
rayluo authored Jul 17, 2017
2 parents 494a672 + d548ec2 commit 03269cf
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 83 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ deploy:
- # test pypi
provider: pypi
distributions: sdist
server: https://testpypi.python.org/pypi
server: https://test.pypi.org/legacy/
user: "nugetaad"
password:
secure: sEaIkr4od/4NJJq/3nvxjC/K/MF/KGBmN148dB4Sj/xMCA3DqqESMxhA8pB4YWfeZHHC6SOTqNHs6SEOxXg5JfY3eOoAke2GV0z/5VaDu/pYbjBZctxi2HC23hcQhDglW1nwfIw8Oglr9VLMbY8lls5AM69E4L/S8vB/hZvwl85WGsLIpod186GT00Ni0W+wnPVNJK+XP7xVb1BM1SRfioVDIsQ9W4qFWEYOLjJQvZByJvkb6Q971rwewZJFihYOZ3ynNeu+3lxifuCqag5KJgRmvaEynMuXx7NhO8kTic+kgcSG2YyDKBWVCrq5/8jJgAAj21n1QuyU3sdPtpVA/yoL41Lr2j1/lIsLbwzJdgaYcwn5QY8VccBNQyGctO/nSvDX6SbFnC1mYUb8J48AFg/EUsTmwqWqkXfBYwSRqgIqeMhh9vGYNh8Fxe9rRaAYTNMQa8ZZ6vBMoO71NhV4WC4tJEJ2KruWqrO9hUFHs4xV0EvAes9uCyzwk/pPE3oCZXt3BlKN5VMlaWzhA06g8oIIuovNnAKeqGXFKRwcVqVoPq+XWYfBy6OLP5b2rTBaYmTMNTkKEbTup66mP/DYbSN/j4g2wuW8koagg1p7a5I+lXrwBaDzLPBeJOxPCdzaU9rZJI8dsvFLDKqUY/wcbE8SoIMLwSwsudzMB5nS1Hw=
secure: Wm2jGolFLm/wrfSPklf9gYdWiTK7ycGr+Qa0voVmFEJkW69PRC5bCibJI3POK1DqTBmQn7gi5G0s117PoLlXvK9lqwMaDL6Yf/ro7YnMU9pBopoB/zWMxWYZeBJVugmTGKuTkbUiQBzL2h0EnaQvvyrEDiLGrYrYEgLUSuR5AVTlvYKk1XBAAhvh8hu8JjgQQugN2ne6ZR9aBjCap0fzdTs3vhad/OQx+iH8YR8UTl4ruszdoL95CDtFmKdIkwg0qgIB65MqC6XAQ2tvhyMDHXZMMafE0NQwUwm2d+sqinCfHLNkb5bVBS0M8syrYCS8xr6Ccnt0PM1+nNFm83bu1w+HaMwKWD2IaU26QH8H7djc7mO1XmRmMSxQ1EjR313YyF534+uiLBlJWB8DOfN4r3/lqg6e44CY0impiT7NnT47bUqaoglew5HB0FgrrtGDrDlLa7zf+RHyb2BhqeqlTR1s0nnzsmzQMdxaHXvCbzYPqg3PUdwLHGBks90tXhA0zUg/3XQfb7v17Lx1byRufvsWWYXUZwLI6H8CCvWtWFvJ3TSPPBR/5LjaICVtt2g3Uv2xrG3weCIO52G7WQ6pIpOyiRsYkUAIXLi2UNsv4LlpNxNObNgL7FNfrNR/tEs8+SdbAkaf2jrFfn+Sk7v4pdPd4og7YXWAE2R/ge9nsJ4=
on:
branch: master
tags: false
Expand All @@ -31,7 +31,7 @@ deploy:
distributions: sdist
user: "nugetaad"
password:
secure: sEaIkr4od/4NJJq/3nvxjC/K/MF/KGBmN148dB4Sj/xMCA3DqqESMxhA8pB4YWfeZHHC6SOTqNHs6SEOxXg5JfY3eOoAke2GV0z/5VaDu/pYbjBZctxi2HC23hcQhDglW1nwfIw8Oglr9VLMbY8lls5AM69E4L/S8vB/hZvwl85WGsLIpod186GT00Ni0W+wnPVNJK+XP7xVb1BM1SRfioVDIsQ9W4qFWEYOLjJQvZByJvkb6Q971rwewZJFihYOZ3ynNeu+3lxifuCqag5KJgRmvaEynMuXx7NhO8kTic+kgcSG2YyDKBWVCrq5/8jJgAAj21n1QuyU3sdPtpVA/yoL41Lr2j1/lIsLbwzJdgaYcwn5QY8VccBNQyGctO/nSvDX6SbFnC1mYUb8J48AFg/EUsTmwqWqkXfBYwSRqgIqeMhh9vGYNh8Fxe9rRaAYTNMQa8ZZ6vBMoO71NhV4WC4tJEJ2KruWqrO9hUFHs4xV0EvAes9uCyzwk/pPE3oCZXt3BlKN5VMlaWzhA06g8oIIuovNnAKeqGXFKRwcVqVoPq+XWYfBy6OLP5b2rTBaYmTMNTkKEbTup66mP/DYbSN/j4g2wuW8koagg1p7a5I+lXrwBaDzLPBeJOxPCdzaU9rZJI8dsvFLDKqUY/wcbE8SoIMLwSwsudzMB5nS1Hw=
secure: Wm2jGolFLm/wrfSPklf9gYdWiTK7ycGr+Qa0voVmFEJkW69PRC5bCibJI3POK1DqTBmQn7gi5G0s117PoLlXvK9lqwMaDL6Yf/ro7YnMU9pBopoB/zWMxWYZeBJVugmTGKuTkbUiQBzL2h0EnaQvvyrEDiLGrYrYEgLUSuR5AVTlvYKk1XBAAhvh8hu8JjgQQugN2ne6ZR9aBjCap0fzdTs3vhad/OQx+iH8YR8UTl4ruszdoL95CDtFmKdIkwg0qgIB65MqC6XAQ2tvhyMDHXZMMafE0NQwUwm2d+sqinCfHLNkb5bVBS0M8syrYCS8xr6Ccnt0PM1+nNFm83bu1w+HaMwKWD2IaU26QH8H7djc7mO1XmRmMSxQ1EjR313YyF534+uiLBlJWB8DOfN4r3/lqg6e44CY0impiT7NnT47bUqaoglew5HB0FgrrtGDrDlLa7zf+RHyb2BhqeqlTR1s0nnzsmzQMdxaHXvCbzYPqg3PUdwLHGBks90tXhA0zUg/3XQfb7v17Lx1byRufvsWWYXUZwLI6H8CCvWtWFvJ3TSPPBR/5LjaICVtt2g3Uv2xrG3weCIO52G7WQ6pIpOyiRsYkUAIXLi2UNsv4LlpNxNObNgL7FNfrNR/tEs8+SdbAkaf2jrFfn+Sk7v4pdPd4og7YXWAE2R/ge9nsJ4=
on:
branch: master
tags: true
Expand Down
39 changes: 24 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,24 @@ The ADAL for python library makes it easy for python applications to authenticat

To support 'service principal' with certificate, ADAL depends on the 'cryptography' package. For smooth installation, some suggestions:

*For Windows and OSX
* For Windows and macOS

Upgrade to the latest pip (8.1.2 as of June 2016) and just do `pip install adal`.

*For Linux
* For Linux

Upgrade to the latest pip (8.1.2 as of June 2016).

You'll need a C compiler, libffi + its development headers, and openssl + its development headers. Refer to [cryptography installation](https://cryptography.io/en/latest/installation/)

*To install from source:
* To install from source:

Upgrade to the latest pip (8.1.2 as of June 2016).
Before run `python setup.py install`, to avoid dealing with compilation errors from cryptography, run `pip install cryptography` first to use statically-linked wheels.
If you still like build from source, refer to [cryptography installation](https://cryptography.io/en/latest/installation/).

For more context, starts with this [stackoverflow thread](http://stackoverflow.com/questions/22073516/failed-to-install-python-cryptography-package-with-pip-and-setup-py).

### About 'client_id' and 'resource' arguments
The convinient methods in 0.1.0 have been removed, and now your application should provide parameter values to `client_id` and `resource`.

2 Reasons:

* Each adal client should have a unique id representing an valid application registered in a tenant. The old methods borrowed the client-id of [azure-cli](https://github.com/Azure/azure-xplat-cli), which is never right. It is simple to register your application and get a client id. Many walkthroughs exist. You can follow [one of those] (http://www.bradygaster.com/post/using-windows-azure-active-directory-to-authenticate-the-management-libraries). Though that involves C# client, but the flow, and particularly the wizard snapshots are the same with adal-python. Do check out if you are new to AAD.

* The old mmethod defaults the `resource` argument to 'https://management.core.windows.net/', now you can just supply this value explictly. Please note, there are lots of different azure resources you can acquire tokens through adal though, for example, the samples in the repository acquire for the 'graph' resource. Because it is not an appropriate assumption to be made at the library level, we removed the old defaults.

### Acquire Token with Client Credentials

In order to use this token acquisition method, you need to configure a service principal. Please follow [this walkthrough](https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-service-principal-portal/).
Expand Down Expand Up @@ -89,13 +83,12 @@ code = context.acquire_user_code(RESOURCE, 'yourClientIdHere')
print(code['message'])
token = context.acquire_token_with_device_code(RESOURCE, code, 'yourClientIdHere')
```

### Acquire Token with authorization code
See the [sample](./sample/website_sample.py) for a complete bare bones web site that makes use of the code below.
```python
context = adal.AuthenticationContext('https://login.microsoftonline.com/ABCDEFGH-1234-1234-1234-ABCDEFGHIJKL')
RESOURCE = '00000002-0000-0000-c000-000000000000' #AAD graph resource
return auth_context.acquire_token_with_authorization_code(
return context.acquire_token_with_authorization_code(
'yourCodeFromQueryString',
'yourWebRedirectUri',
RESOURCE,
Expand All @@ -104,7 +97,11 @@ return auth_context.acquire_token_with_authorization_code(
```

## Samples and Documentation
[We provide a full suite of sample applications and documentation on GitHub](https://github.com/AzureADSamples) to help you get started with learning the Azure Identity system. This includes tutorials for native clients such as Windows, Windows Phone, iOS, OSX, Android, and Linux. We also provide full walkthroughs for authentication flows such as OAuth2, OpenID Connect, Graph API, and other awesome features.
We provide a full suite of [sample applications on GitHub](https://github.com/azure-samples?utf8=%E2%9C%93&q=active-directory&type=&language=) and an [Azure AD developer landing page](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-developers-guide) to help you get started with learning the Azure Identity system. This includes tutorials for native clients and web applications. We also provide full walkthroughs for authentication flows such as OAuth2, OpenID Connect and for calling APIs such as the Graph API.

It is recommended to read the [Auth Scenarios](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios) doc, specifically the [Scenarios section](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios#application-types-and-scenarios). For some topics about registering/integrating an app, checkout [this doc](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications). And finally, we have a great topic on the Auth protocols you would be using and how they play with Azure AD in [this doc](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code).

While Python-specific samples will be added into the aforementioned documents as an on-going effort, you can always find [most relevant samples just inside this library repo](https://github.com/AzureAD/azure-activedirectory-library-for-python/tree/dev/sample).

## Community Help and Support

Expand Down Expand Up @@ -132,3 +129,15 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope

### http tracing/proxy
If need to bypass self-signed certificates, turn on the environment variable of `ADAL_PYTHON_SSL_NO_VERIFY`


## Note

### Changes on 'client_id' and 'resource' arguments after 0.1.0
The convinient methods in 0.1.0 have been removed, and now your application should provide parameter values to `client_id` and `resource`.

2 Reasons:

* Each adal client should have an Application ID representing an valid application registered in a tenant. The old methods borrowed the client-id of [azure-cli](https://github.com/Azure/azure-xplat-cli), which is never right. It is simple to register your application and get a client id. Many walkthroughs exist. You can follow [one of those](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications). Do check out if you are new to AAD.

* The old method defaults the `resource` argument to 'https://management.core.windows.net/', now you can just supply this value explictly. Please note, there are lots of different azure resources you can acquire tokens through adal though, for example, the samples in the repository acquire for the 'graph' resource. Because it is not an appropriate assumption to be made at the library level, we removed the old defaults.
2 changes: 1 addition & 1 deletion adal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

# pylint: disable=wrong-import-position

__version__ = '0.4.5'
__version__ = '0.4.6'

import logging

Expand Down
16 changes: 12 additions & 4 deletions adal/cache_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,19 @@ def _refresh_entry_if_necessary(self, entry, is_resource_specific):
now_plus_buffer = now + timedelta(minutes=Misc.CLOCK_BUFFER)

if is_resource_specific and now_plus_buffer > expiry_date:
self._log.info('Cached token is expired. Refreshing: %s', expiry_date)
return self._refresh_expired_entry(entry)
if TokenResponseFields.REFRESH_TOKEN in entry:
self._log.info('Cached token is expired. Refreshing: %s', expiry_date)
return self._refresh_expired_entry(entry)
else:
self.remove(entry)
return None
elif not is_resource_specific and entry.get(TokenResponseFields.IS_MRRT):
self._log.info('Acquiring new access token from MRRT token.')
return self._acquire_new_token_from_mrrt(entry)
if TokenResponseFields.REFRESH_TOKEN in entry:
self._log.info('Acquiring new access token from MRRT token.')
return self._acquire_new_token_from_mrrt(entry)
else:
self.remove(entry)
return None
else:
return entry

Expand Down
9 changes: 8 additions & 1 deletion adal/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,14 @@ class HttpError(object):
class AADConstants(object):

WORLD_WIDE_AUTHORITY = 'login.windows.net'
WELL_KNOWN_AUTHORITY_HOSTS = ['login.windows.net', 'login.microsoftonline.com', 'login.chinacloudapi.cn', 'login-us.microsoftonline.com', 'login.microsoftonline.de']
WELL_KNOWN_AUTHORITY_HOSTS = [
'login.windows.net',
'login.microsoftonline.com',
'login.chinacloudapi.cn',
'login-us.microsoftonline.com',
'login.microsoftonline.us',
'login.microsoftonline.de',
]
INSTANCE_DISCOVERY_ENDPOINT_TEMPLATE = 'https://{authorize_host}/common/discovery/instance?authorization_endpoint={authorize_endpoint}&api-version=1.0' # pylint: disable=invalid-name
AUTHORIZE_ENDPOINT_PATH = '/oauth2/authorize'
TOKEN_ENDPOINT_PATH = '/oauth2/token'
Expand Down
4 changes: 2 additions & 2 deletions adal/oauth2_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ def _validate_token_response(self, body):
wire_response[OAuth2.ResponseParameters.CREATED_ON] = str(temp_date)

if not wire_response.get(OAuth2.ResponseParameters.TOKEN_TYPE):
raise AdalError('wire_response is missing token_type')
raise AdalError('wire_response is missing token_type', wire_response)

if not wire_response.get(OAuth2.ResponseParameters.ACCESS_TOKEN):
raise AdalError('wire_response is missing access_token')
raise AdalError('wire_response is missing access_token', wire_response)

token_response = map_fields(wire_response, TOKEN_RESPONSE_MAP)

Expand Down
24 changes: 13 additions & 11 deletions sample/certificate_credentials_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

def turn_on_logging():
logging.basicConfig(level=logging.DEBUG)
#or,
#or,
#handler = logging.StreamHandler()
#adal.set_logging_options({
# 'level': 'DEBUG',
# 'handler': handler
# 'handler': handler
#})
#handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))

Expand All @@ -20,19 +20,20 @@ def get_private_key(filename):
return private_pem

#
# You can provide account information by using a JSON file. Either
# through a command line argument, 'python sample.js parameters.json', or
# You can provide account information by using a JSON file. Either
# through a command line argument, 'python sample.py parameters.json', or
# specifying in an environment variable of ADAL_SAMPLE_PARAMETERS_FILE.
# privateKeyFile must contain a PEM encoded cert with private key.
# thumbprint must be the thumbprint of the privateKeyFile.
# {
# "resource": "your_resource",
# "tenant" : "naturalcauses.onmicrosoft.com",
# "authorityHostUrl" : "https://login.microsoftonline.com",
# "clientId" : "d6835713-b745-48d1-bb62-7a8248477d35",
# "thumbprint" : 'C15DEA8656ADDF67BE8031D85EBDDC5AD6C436E1',
# "privateKeyFile" : 'ncwebCTKey.pem'
# }
parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
os.environ.get('ADAL_SAMPLE_PARAMETERS_FILE'))
sample_parameters = {}
if parameters_file:
Expand All @@ -43,20 +44,21 @@ def get_private_key(filename):
raise ValueError('Please provide parameter file with account information.')


authority_url = (sample_parameters['authorityHostUrl'] + '/' +
authority_url = (sample_parameters['authorityHostUrl'] + '/' +
sample_parameters['tenant'])
RESOURCE = '00000002-0000-0000-c000-000000000000'
GRAPH_RESOURCE = '00000002-0000-0000-c000-000000000000'
RESOURCE = sample_parameters.get('resource', GRAPH_RESOURCE)

#uncomment for verbose logging
turn_on_logging()

context = adal.AuthenticationContext(authority_url)
context = adal.AuthenticationContext(authority_url, api_version=None)
key = get_private_key(sample_parameters['privateKeyFile'])

token = context.acquire_token_with_client_certificate(
RESOURCE,
sample_parameters['clientId'],
key,
RESOURCE,
sample_parameters['clientId'],
key,
sample_parameters['thumbprint'])

print('Here is the token:')
Expand Down
22 changes: 13 additions & 9 deletions sample/client_credentials_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@

def turn_on_logging():
logging.basicConfig(level=logging.DEBUG)
#or,
#or,
#handler = logging.StreamHandler()
#adal.set_logging_options({
# 'level': 'DEBUG',
# 'handler': handler
# 'handler': handler
#})
#handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))

# You can provide account information by using a JSON file. Either
# through a command line argument, 'python sample.js parameters.json', or
# through a command line argument, 'python sample.py parameters.json', or
# specifying in an environment variable of ADAL_SAMPLE_PARAMETERS_FILE.
# {
# "resource": "your_resource",
# "tenant" : "rrandallaad1.onmicrosoft.com",
# "authorityHostUrl" : "https://login.microsoftonline.com",
# "clientId" : "624ac9bd-4c1c-4687-aec8-b56a8991cfb3",
# "clientSecret" : "verySecret=""
# }

parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
os.environ.get('ADAL_SAMPLE_PARAMETERS_FILE'))

if parameters_file:
Expand All @@ -33,19 +34,22 @@ def turn_on_logging():
sample_parameters = json.loads(parameters)
else:
raise ValueError('Please provide parameter file with account information.')
authority_url = (sample_parameters['authorityHostUrl'] + '/' +

authority_url = (sample_parameters['authorityHostUrl'] + '/' +
sample_parameters['tenant'])
RESOURCE = '00000002-0000-0000-c000-000000000000'
GRAPH_RESOURCE = '00000002-0000-0000-c000-000000000000'
RESOURCE = sample_parameters.get('resource', GRAPH_RESOURCE)

#uncomment for verbose log
#turn_on_logging()

context = adal.AuthenticationContext(authority_url)
context = adal.AuthenticationContext(
authority_url, validate_authority=sample_parameters['tenant'] != 'adfs',
api_version=None)

token = context.acquire_token_with_client_credentials(
RESOURCE,
sample_parameters['clientId'],
sample_parameters['clientId'],
sample_parameters['clientSecret'])

print('Here is the token:')
Expand Down
18 changes: 10 additions & 8 deletions sample/device_code_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@

def turn_on_logging():
logging.basicConfig(level=logging.DEBUG)
#or,
#or,
#handler = logging.StreamHandler()
#adal.set_logging_options({
# 'level': 'DEBUG',
# 'handler': handler
# 'handler': handler
#})
#handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))

# You can provide account information by using a JSON file
# with the same parameters as the sampleParameters variable below. Either
# through a command line argument, 'python sample.js parameters.json', or
# through a command line argument, 'python sample.py parameters.json', or
# specifying in an environment variable of ADAL_SAMPLE_PARAMETERS_FILE.
# {
# "resource": "your_resource",
# "tenant" : "rrandallaad1.onmicrosoft.com",
# "authorityHostUrl" : "https://login.microsoftonline.com",
# "clientid" : "",
# "anothertenant" : "bar.onmicrosoft.com"
# }

parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
os.environ.get('ADAL_SAMPLE_PARAMETERS_FILE'))

if parameters_file:
Expand All @@ -39,12 +40,13 @@ def turn_on_logging():
authority_host_url = sample_parameters['authorityHostUrl']
authority_url = authority_host_url + '/' + sample_parameters['tenant']
clientid = sample_parameters['clientid']
RESOURCE = '00000002-0000-0000-c000-000000000000'
GRAPH_RESOURCE = '00000002-0000-0000-c000-000000000000'
RESOURCE = sample_parameters.get('resource', GRAPH_RESOURCE)

#uncomment for verbose logging
#uncomment for verbose logging
#turn_on_logging()

context = adal.AuthenticationContext(authority_url)
context = adal.AuthenticationContext(authority_url, api_version=None)
code = context.acquire_user_code(RESOURCE, clientid)
print(code['message'])
token = context.acquire_token_with_device_code(RESOURCE, code, clientid)
Expand All @@ -56,7 +58,7 @@ def turn_on_logging():
another_tenant = sample_parameters.get('anothertenant')
if another_tenant:
authority_url = authority_host_url + '/' + another_tenant
#reuse existing cache which has the tokens acquired early on
#reuse existing cache which has the tokens acquired early on
existing_cache = context.cache
context = adal.AuthenticationContext(authority_url, cache=existing_cache)
token = context.acquire_token(RESOURCE, token['userId'], clientid)
Expand Down
Loading

0 comments on commit 03269cf

Please sign in to comment.