Skip to content

Commit 928d4ea

Browse files
authored
Merge pull request #346 from AzureAD/release-1.11.0
MSAL Python 1.11.0
2 parents 3b9b6aa + 6320055 commit 928d4ea

File tree

9 files changed

+506
-203
lines changed

9 files changed

+506
-203
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Not sure whether this is the SDK you are looking for your app? There are other M
1212

1313
Quick links:
1414

15-
| [Getting Started](https://docs.microsoft.com/azure/active-directory/develop/quickstart-v2-python-webapp) | [Docs](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki) | [Samples](https://aka.ms/aaddevsamplesv2) | [Support](README.md#community-help-and-support)
16-
| --- | --- | --- | --- |
15+
| [Getting Started](https://docs.microsoft.com/azure/active-directory/develop/quickstart-v2-python-webapp) | [Docs](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki) | [Samples](https://aka.ms/aaddevsamplesv2) | [Support](README.md#community-help-and-support) | [Feedback](https://forms.office.com/r/TMjZkDbzjY) |
16+
| --- | --- | --- | --- | --- |
1717

1818
## Installation
1919

@@ -126,6 +126,9 @@ We recommend you use the "msal" tag so we can see it!
126126
Here is the latest Q&A on Stack Overflow for MSAL:
127127
[http://stackoverflow.com/questions/tagged/msal](http://stackoverflow.com/questions/tagged/msal)
128128

129+
## Submit Feedback
130+
We'd like your thoughts on this library. Please complete [this short survey.](https://forms.office.com/r/TMjZkDbzjY)
131+
129132
## Security Reporting
130133

131134
If you find a security issue with our libraries or services please report it to [[email protected]](mailto:[email protected]) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://technet.microsoft.com/security/dd252948) and subscribing to Security Advisory Alerts.

msal/application.py

Lines changed: 178 additions & 162 deletions
Large diffs are not rendered by default.

msal/oauth2cli/authcode.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,34 @@ def obtain_auth_code(listen_port, auth_uri=None): # Historically only used in t
3333
).get("code")
3434

3535

36+
def is_wsl():
37+
# "Official" way of detecting WSL: https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364
38+
# Run `uname -a` to get 'release' without python
39+
# - WSL 1: '4.4.0-19041-Microsoft'
40+
# - WSL 2: '4.19.128-microsoft-standard'
41+
import platform
42+
uname = platform.uname()
43+
platform_name = getattr(uname, 'system', uname[0]).lower()
44+
release = getattr(uname, 'release', uname[2]).lower()
45+
return platform_name == 'linux' and 'microsoft' in release
46+
47+
3648
def _browse(auth_uri): # throws ImportError, possibly webbrowser.Error in future
3749
import webbrowser # Lazy import. Some distro may not have this.
38-
return webbrowser.open(auth_uri) # Use default browser. Customizable by $BROWSER
50+
browser_opened = webbrowser.open(auth_uri) # Use default browser. Customizable by $BROWSER
51+
52+
# In WSL which doesn't have www-browser, try launching browser with PowerShell
53+
if not browser_opened and is_wsl():
54+
try:
55+
import subprocess
56+
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe
57+
# Ampersand (&) should be quoted
58+
exit_code = subprocess.call(
59+
['powershell.exe', '-NoProfile', '-Command', 'Start-Process "{}"'.format(auth_uri)])
60+
browser_opened = exit_code == 0
61+
except FileNotFoundError: # WSL might be too old
62+
pass
63+
return browser_opened
3964

4065

4166
def _qs2kv(qs):
@@ -245,4 +270,3 @@ def __exit__(self, exc_type, exc_val, exc_tb):
245270
timeout=60,
246271
state=flow["state"], # Optional
247272
), indent=4))
248-

msal/telemetry.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import uuid
2+
import logging
3+
4+
5+
logger = logging.getLogger(__name__)
6+
7+
CLIENT_REQUEST_ID = 'client-request-id'
8+
CLIENT_CURRENT_TELEMETRY = "x-client-current-telemetry"
9+
CLIENT_LAST_TELEMETRY = "x-client-last-telemetry"
10+
NON_SILENT_CALL = 0
11+
FORCE_REFRESH = 1
12+
AT_ABSENT = 2
13+
AT_EXPIRED = 3
14+
AT_AGING = 4
15+
RESERVED = 5
16+
17+
18+
def _get_new_correlation_id():
19+
return str(uuid.uuid4())
20+
21+
22+
class _TelemetryContext(object):
23+
"""It is used for handling the telemetry context for current OAuth2 "exchange"."""
24+
# https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=%2FTelemetry%2FMSALServerSideTelemetry.md&_a=preview
25+
_SUCCEEDED = "succeeded"
26+
_FAILED = "failed"
27+
_FAILURE_SIZE = "failure_size"
28+
_CURRENT_HEADER_SIZE_LIMIT = 100
29+
_LAST_HEADER_SIZE_LIMIT = 350
30+
31+
def __init__(self, buffer, lock, api_id, correlation_id=None, refresh_reason=None):
32+
self._buffer = buffer
33+
self._lock = lock
34+
self._api_id = api_id
35+
self._correlation_id = correlation_id or _get_new_correlation_id()
36+
self._refresh_reason = refresh_reason or NON_SILENT_CALL
37+
logger.debug("Generate or reuse correlation_id: %s", self._correlation_id)
38+
39+
def generate_headers(self):
40+
with self._lock:
41+
current = "4|{api_id},{cache_refresh}|".format(
42+
api_id=self._api_id, cache_refresh=self._refresh_reason)
43+
if len(current) > self._CURRENT_HEADER_SIZE_LIMIT:
44+
logger.warning(
45+
"Telemetry header greater than {} will be truncated by AAD".format(
46+
self._CURRENT_HEADER_SIZE_LIMIT))
47+
failures = self._buffer.get(self._FAILED, [])
48+
return {
49+
CLIENT_REQUEST_ID: self._correlation_id,
50+
CLIENT_CURRENT_TELEMETRY: current,
51+
CLIENT_LAST_TELEMETRY: "4|{succeeded}|{failed_requests}|{errors}|".format(
52+
succeeded=self._buffer.get(self._SUCCEEDED, 0),
53+
failed_requests=",".join("{a},{c}".format(**f) for f in failures),
54+
errors=",".join(f["e"] for f in failures),
55+
)
56+
}
57+
58+
def hit_an_access_token(self):
59+
with self._lock:
60+
self._buffer[self._SUCCEEDED] = self._buffer.get(self._SUCCEEDED, 0) + 1
61+
62+
def update_telemetry(self, auth_result):
63+
if auth_result:
64+
with self._lock:
65+
if "error" in auth_result:
66+
self._record_failure(auth_result["error"])
67+
else: # Telemetry sent successfully. Reset buffer
68+
self._buffer.clear() # This won't work: self._buffer = {}
69+
70+
def _record_failure(self, error):
71+
simulation = len(",{api_id},{correlation_id},{error}".format(
72+
api_id=self._api_id, correlation_id=self._correlation_id, error=error))
73+
if self._buffer.get(self._FAILURE_SIZE, 0) + simulation < self._LAST_HEADER_SIZE_LIMIT:
74+
self._buffer[self._FAILURE_SIZE] = self._buffer.get(
75+
self._FAILURE_SIZE, 0) + simulation
76+
self._buffer.setdefault(self._FAILED, []).append({
77+
"a": self._api_id, "c": self._correlation_id, "e": error})
78+

msal/token_cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def __add(self, event, now=None):
145145
client_info["uid"] = id_token_claims.get("sub")
146146
home_account_id = id_token_claims.get("sub")
147147

148-
target = ' '.join(event.get("scope", [])) # Per schema, we don't sort it
148+
target = ' '.join(event.get("scope") or []) # Per schema, we don't sort it
149149

150150
with self._lock:
151151
now = int(time.time() if now is None else now)

msal/wstrust_response.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,7 @@ def parse_token_by_re(raw_response): # Returns the saml:assertion
8888
token_types = findall_content(rstr, "TokenType")
8989
tokens = findall_content(rstr, "RequestedSecurityToken")
9090
if token_types and tokens:
91-
return {"token": tokens[0].encode('us-ascii'), "type": token_types[0]}
91+
# Historically, we use "us-ascii" encoding, but it should be "utf-8"
92+
# https://stackoverflow.com/questions/36658000/what-is-encoding-used-for-saml-conversations
93+
return {"token": tokens[0].encode('utf-8'), "type": token_types[0]}
9294

sample/username_password_sample.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
config = json.load(open(sys.argv[1]))
3535

3636
# Create a preferably long-lived app instance which maintains a token cache.
37-
app = msal.PublicClientApplication(
37+
app = msal.ClientApplication(
3838
config["client_id"], authority=config["authority"],
39+
client_credential=config.get("client_secret"),
3940
# token_cache=... # Default cache is in memory only.
4041
# You can learn how to use SerializableTokenCache from
4142
# https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache

0 commit comments

Comments
 (0)