Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6abbc74
Fix typos
rayluo Jun 22, 2020
0faf132
Merge branch 'fix-typos' into dev
rayluo Jun 22, 2020
da69a9f
Merge pull request #218 from AzureAD/release-1.4.1
abhidnya13 Jun 26, 2020
2da6f4a
nbf validation is implicitly required by JWT specs
rayluo Jun 29, 2020
7a23a58
Merge branch 'jwt-validation-for-nbf' into dev
rayluo Jun 29, 2020
969877f
Merge remote branch into dev
rayluo Jun 29, 2020
358e7d2
Update issue templates
rayluo Jun 30, 2020
37edf48
Merge pull request #219 from AzureAD/issue-template
rayluo Jul 1, 2020
27b72c8
Removing hardcoded client_ids from test environment (#220)
abhidnya13 Jul 6, 2020
61ac5ca
Adding header
abhidnya13 Jul 7, 2020
1947355
Merge pull request #224 from AzureAD/add-telemetry-id
abhidnya13 Jul 7, 2020
484172d
Update to the yet another latest lab api page
rayluo Jul 10, 2020
179f87e
change
abhidnya13 Jul 13, 2020
3d477ab
Merge pull request #230 from AzureAD/ws-trust-request-change
abhidnya13 Jul 15, 2020
0ced4cb
Removing content type header for GET requests to Mex endpoint (#227)
abhidnya13 Jul 16, 2020
11163f1
Adding assert statement for verifying
abhidnya13 Jul 7, 2020
0c5c807
An optional tuning happens to also bypass a malfunctioning alias
rayluo Jul 10, 2020
2f5774c
Merge pull request #225 from AzureAD/arlington-test-case
rayluo Jul 20, 2020
794384c
Fix a bug found by https://github.com/OpenIDC/pyoidc/issues/754#issue…
rayluo Jul 21, 2020
4c745f8
Merge branch 'http-basic-auth-after-url-encoding' into dev
rayluo Jul 21, 2020
ff7629b
Merge remote branch into adopt-url-encoding
rayluo Jul 21, 2020
7aefb84
Merge branch 'adopt-url-encoding' into dev
rayluo Jul 21, 2020
d473cb0
MSAL Python 1.4.2
abhidnya13 Jul 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to our [off-the-shelf samples](https://github.com/AzureAD/microsoft-authentication-library-for-python/tree/dev/sample) and pick one that is closest to your usage scenario. You should not need to modify the sample.
2. Follow the description of the sample, typically at the beginning of it, to prepare a `config.json` containing your test configurations
3. Run such sample, typically by `python sample.py config.json`
4. See the error
5. In this bug report, tell us the sample you choose, paste the content of the config.json with your test setup (which you can choose to skip your credentials, and/or mail it to our developer's email).

**Expected behavior**
A clear and concise description of what you expected to happen.

**What you see instead**
Paste the sample output, or add screenshots to help explain your problem.

**The MSAL Python version you are using**
Paste the output of this
`python -c "import msal; print(msal.__version__)"`

**Additional context**
Add any other context about the problem here.
14 changes: 13 additions & 1 deletion msal/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


# The __init__.py will import this. Not the other way around.
__version__ = "1.4.1"
__version__ = "1.4.2"

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -82,6 +82,7 @@ def extract_certs(public_cert_content):
class ClientApplication(object):

ACQUIRE_TOKEN_SILENT_ID = "84"
ACQUIRE_TOKEN_BY_REFRESH_TOKEN = "85"
ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID = "301"
ACQUIRE_TOKEN_ON_BEHALF_OF_ID = "523"
ACQUIRE_TOKEN_BY_DEVICE_FLOW_ID = "622"
Expand Down Expand Up @@ -551,6 +552,12 @@ def acquire_token_silent_with_error(
return result
final_result = result
for alias in self._get_authority_aliases(self.authority.instance):
if not self.token_cache.find(
self.token_cache.CredentialType.REFRESH_TOKEN,
target=scopes,
query={"environment": alias}):
# Skip heavy weight logic when RT for this alias doesn't exist
continue
the_authority = Authority(
"https://" + alias + "/" + self.authority.tenant,
self.http_client,
Expand Down Expand Up @@ -727,6 +734,11 @@ def acquire_token_by_refresh_token(self, refresh_token, scopes):
return self.client.obtain_token_by_refresh_token(
refresh_token,
scope=decorate_scope(scopes, self.client_id),
headers={
CLIENT_REQUEST_ID: _get_new_correlation_id(),
CLIENT_CURRENT_TELEMETRY: _build_current_telemetry_request_header(
self.ACQUIRE_TOKEN_BY_REFRESH_TOKEN),
},
rt_getter=lambda rt: rt,
on_updating_rt=False,
on_removing_rt=lambda rt_item: None, # No OP
Expand Down
4 changes: 1 addition & 3 deletions msal/mex.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ def _xpath_of_root(route_to_leaf):


def send_request(mex_endpoint, http_client, **kwargs):
mex_document = http_client.get(
mex_endpoint, headers={'Content-Type': 'application/soap+xml'},
**kwargs).text
mex_document = http_client.get(mex_endpoint, **kwargs).text
return Mex(mex_document).get_wstrust_username_password_endpoint()


Expand Down
15 changes: 10 additions & 5 deletions msal/oauth2cli/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

import json
try:
from urllib.parse import urlencode, parse_qs
from urllib.parse import urlencode, parse_qs, quote_plus
except ImportError:
from urlparse import parse_qs
from urllib import urlencode
from urllib import urlencode, quote_plus
import logging
import warnings
import time
Expand Down Expand Up @@ -205,9 +205,14 @@ def _obtain_token( # The verb "obtain" is influenced by OAUTH2 RFC 6749
# client credentials in the request-body using the following
# parameters: client_id, client_secret.
if self.client_secret and self.client_id:
_headers["Authorization"] = "Basic " + base64.b64encode(
"{}:{}".format(self.client_id, self.client_secret)
.encode("ascii")).decode("ascii")
_headers["Authorization"] = "Basic " + base64.b64encode("{}:{}".format(
# Per https://tools.ietf.org/html/rfc6749#section-2.3.1
# client_id and client_secret needs to be encoded by
# "application/x-www-form-urlencoded"
# https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
# BEFORE they are fed into HTTP Basic Authentication
quote_plus(self.client_id), quote_plus(self.client_secret)
).encode("ascii")).decode("ascii")

if "token_endpoint" not in self.configuration:
raise ValueError("token_endpoint not found in configuration")
Expand Down
7 changes: 6 additions & 1 deletion msal/oauth2cli/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def decode_id_token(id_token, client_id=None, issuer=None, nonce=None, now=None)
"""
decoded = json.loads(decode_part(id_token.split('.')[1]))
err = None # https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
_now = now or time.time()
if _now < decoded.get("nbf", _now - 1): # nbf is optional per JWT specs
# This is not an ID token validation, but a JWT validation
# https://tools.ietf.org/html/rfc7519#section-4.1.5
err = "0. The ID token is not yet valid"
if issuer and issuer != decoded["iss"]:
# https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
err = ('2. The Issuer Identifier for the OpenID Provider, "%s", '
Expand All @@ -53,7 +58,7 @@ def decode_id_token(id_token, client_id=None, issuer=None, nonce=None, now=None)
# the Client and the Token Endpoint (which it is in this flow),
# the TLS server validation MAY be used to validate the issuer
# in place of checking the token signature.
if (now or time.time()) > decoded["exp"]:
if _now > decoded["exp"]:
err = "9. The current time MUST be before the time represented by the exp Claim."
if nonce and nonce != decoded.get("nonce"):
err = ("11. Nonce must be the same value "
Expand Down
2 changes: 1 addition & 1 deletion msal/wstrust_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _build_rst(username, password, cloud_audience_urn, endpoint_address, soap_ac
return """<s:Envelope xmlns:s='{s}' xmlns:wsa='{wsa}' xmlns:wsu='{wsu}'>
<s:Header>
<wsa:Action s:mustUnderstand='1'>{soap_action}</wsa:Action>
<wsa:messageID>urn:uuid:{message_id}</wsa:messageID>
<wsa:MessageID>urn:uuid:{message_id}</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:ReplyTo>
Expand Down
35 changes: 26 additions & 9 deletions tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def get_lab_app(
# or it could be setup on Travis CI
# https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings
# Data came from here
# https://microsoft.sharepoint-df.com/teams/MSIDLABSExtended/SitePages/Rese.aspx#programmatic-access-info-for-lab-request-api
# https://docs.msidlab.com/accounts/confidentialclient.html
logger.info("Using lab app defined by ENV variables %s and %s",
env_client_id, env_client_secret)
client_id = os.getenv(env_client_id)
Expand Down Expand Up @@ -482,7 +482,6 @@ def test_ropc_adfs2019_onprem(self):
# Configuration is derived from https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/4.7.0/tests/Microsoft.Identity.Test.Common/TestConstants.cs#L250-L259
config = self.get_lab_user(usertype="onprem", federationProvider="ADFSv2019")
config["authority"] = "https://fs.%s.com/adfs" % config["lab_name"]
config["client_id"] = "PublicClientId"
config["scope"] = self.adfs2019_scopes
config["password"] = self.get_lab_user_secret(config["lab_name"])
self._test_username_password(**config)
Expand All @@ -497,26 +496,31 @@ def test_adfs2019_onprem_acquire_token_by_auth_code(self):
"""
config = self.get_lab_user(usertype="onprem", federationProvider="ADFSv2019")
config["authority"] = "https://fs.%s.com/adfs" % config["lab_name"]
config["client_id"] = "PublicClientId"
config["scope"] = self.adfs2019_scopes
config["port"] = 8080
self._test_acquire_token_by_auth_code(**config)

@unittest.skipUnless(
os.getenv("LAB_OBO_CLIENT_SECRET"),
"Need LAB_OBO_CLIENT SECRET from https://msidlabs.vault.azure.net/secrets/TodoListServiceV2-OBO/c58ba97c34ca4464886943a847d1db56")
@unittest.skipUnless(
os.getenv("LAB_OBO_CONFIDENTIAL_CLIENT_ID"),
"Confidential client id can be found here https://docs.msidlab.com/flows/onbehalfofflow.html")
@unittest.skipUnless(
os.getenv("LAB_OBO_PUBLIC_CLIENT_ID"),
"Public client id can be found here https://docs.msidlab.com/flows/onbehalfofflow.html")
def test_acquire_token_obo(self):
config = self.get_lab_user(usertype="cloud")

config_cca = {}
config_cca.update(config)
config_cca["client_id"] = "f4aa5217-e87c-42b2-82af-5624dd14ee72"
config_cca["client_id"] = os.getenv("LAB_OBO_CONFIDENTIAL_CLIENT_ID")
config_cca["scope"] = ["https://graph.microsoft.com/.default"]
config_cca["client_secret"] = os.getenv("LAB_OBO_CLIENT_SECRET")

config_pca = {}
config_pca.update(config)
config_pca["client_id"] = "c0485386-1e9a-4663-bc96-7ab30656de7f"
config_pca["client_id"] = os.getenv("LAB_OBO_PUBLIC_CLIENT_ID")
config_pca["password"] = self.get_lab_user_secret(config_pca["lab_name"])
config_pca["scope"] = ["api://%s/read" % config_cca["client_id"]]

Expand All @@ -535,20 +539,22 @@ def test_b2c_acquire_token_by_auth_code(self):
# This won't work https://msidlab.com/api/user?usertype=b2c
password="***" # From https://aka.ms/GetLabUserSecret?Secret=msidlabb2c
"""
config = self.get_lab_app_object(azureenvironment="azureb2ccloud")
self._test_acquire_token_by_auth_code(
authority=self._build_b2c_authority("B2C_1_SignInPolicy"),
client_id="b876a048-55a5-4fc5-9403-f5d90cb1c852",
client_id=config["appId"],
port=3843, # Lab defines 4 of them: [3843, 4584, 4843, 60000]
scope=["https://msidlabb2c.onmicrosoft.com/msaapp/user_impersonation"]
scope=config["defaultScopes"].split(','),
)

def test_b2c_acquire_token_by_ropc(self):
config = self.get_lab_app_object(azureenvironment="azureb2ccloud")
self._test_username_password(
authority=self._build_b2c_authority("B2C_1_ROPC_Auth"),
client_id="e3b9ad76-9763-4827-b088-80c7a7888f79",
client_id=config["appId"],
username="b2clocal@msidlabb2c.onmicrosoft.com",
password=self.get_lab_user_secret("msidlabb2c"),
scope=["https://msidlabb2c.onmicrosoft.com/msidlabb2capi/read"],
scope=config["defaultScopes"].split(','),
)


Expand Down Expand Up @@ -584,6 +590,17 @@ def test_acquire_token_device_flow(self):
config["scope"] = ["user.read"]
self._test_device_flow(**config)

def test_acquire_token_silent_with_an_empty_cache_should_return_none(self):
config = self.get_lab_user(
usertype="cloud", azureenvironment=self.environment, publicClient="no")
app = msal.ConfidentialClientApplication(
config['client_id'], authority=config['authority'],
http_client=MinimalHttpClient())
result = app.acquire_token_silent(scopes=config['scope'], account=None)
self.assertEqual(result, None)
# Note: An alias in this region is no longer accepting HTTPS traffic.
# If this test case passes without exception,
# it means MSAL Python is not affected by that.

if __name__ == "__main__":
unittest.main()
Expand Down