Skip to content

MSAL Python 1.11.0 #346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8885593
Merge pull request #322 from AzureAD/release-1.10.0
rayluo Mar 8, 2021
d45030b
Fix typo in docstring
jiasli Mar 9, 2021
a165085
Merge pull request #323 from jiasli/scopes
rayluo Mar 10, 2021
a81ae55
Remove inaccurate doc due to copy-and-paste error
rayluo Mar 17, 2021
d3b772c
Merge pull request #327 from AzureAD/fix-doc-copy-paste-error
rayluo Mar 17, 2021
090270d
Enable retry on connection error
rayluo Mar 16, 2021
9d29158
Merge pull request #326 from AzureAD/retry-connection-error
rayluo Mar 18, 2021
31b24af
Implementing Telemetry V4
rayluo Feb 24, 2021
5ccf410
Merge pull request #329 from AzureAD/telemetry-v4
rayluo Mar 29, 2021
42b9fc1
Support launching browser in WSL Ubuntu 18.04 (#333)
jiasli Mar 30, 2021
afd5963
Saml token should use utf-8 encoding
rayluo Mar 25, 2021
c0e1da9
Merge pull request #336 from AzureAD/saml-utf8
rayluo Mar 31, 2021
0f1ab8d
Fix type hint for `client_credential`
jiasli Apr 1, 2021
a37c6fe
Merge pull request #337 from jiasli/type-hint
rayluo Apr 1, 2021
9a0192d
survey added to README.md
Apr 6, 2021
093db3b
Merge pull request #340 from ShannonCanTech/loyalty-survey-readme
rayluo Apr 7, 2021
c8abd3a
Move ROPC from PCA to base
rayluo Mar 13, 2021
a8011f6
Merge pull request #344 from AzureAD/confidential-ropc
rayluo Apr 8, 2021
c291a94
Fix typo
rayluo Apr 8, 2021
072ca58
Merge pull request #345 from AzureAD/telemetry-v4
rayluo Apr 8, 2021
99f3b07
Bumps version number
rayluo Apr 8, 2021
87fac90
Doc for the new retry-on-connection-error behavior
rayluo Apr 8, 2021
1122cb7
Merge branch 'retry-connection-error' into dev
rayluo Apr 8, 2021
6320055
Merge branch 'dev' into release-1.11.0
rayluo Apr 8, 2021
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
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Not sure whether this is the SDK you are looking for your app? There are other M

Quick links:

| [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)
| --- | --- | --- | --- |
| [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) |
| --- | --- | --- | --- | --- |

## Installation

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

## Submit Feedback
We'd like your thoughts on this library. Please complete [this short survey.](https://forms.office.com/r/TMjZkDbzjY)

## Security Reporting

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.
Expand Down
340 changes: 178 additions & 162 deletions msal/application.py

Large diffs are not rendered by default.

28 changes: 26 additions & 2 deletions msal/oauth2cli/authcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,34 @@ def obtain_auth_code(listen_port, auth_uri=None): # Historically only used in t
).get("code")


def is_wsl():
# "Official" way of detecting WSL: https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364
# Run `uname -a` to get 'release' without python
# - WSL 1: '4.4.0-19041-Microsoft'
# - WSL 2: '4.19.128-microsoft-standard'
import platform
uname = platform.uname()
platform_name = getattr(uname, 'system', uname[0]).lower()
release = getattr(uname, 'release', uname[2]).lower()
return platform_name == 'linux' and 'microsoft' in release


def _browse(auth_uri): # throws ImportError, possibly webbrowser.Error in future
import webbrowser # Lazy import. Some distro may not have this.
return webbrowser.open(auth_uri) # Use default browser. Customizable by $BROWSER
browser_opened = webbrowser.open(auth_uri) # Use default browser. Customizable by $BROWSER

# In WSL which doesn't have www-browser, try launching browser with PowerShell
if not browser_opened and is_wsl():
try:
import subprocess
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe
# Ampersand (&) should be quoted
exit_code = subprocess.call(
['powershell.exe', '-NoProfile', '-Command', 'Start-Process "{}"'.format(auth_uri)])
browser_opened = exit_code == 0
except FileNotFoundError: # WSL might be too old
pass
return browser_opened


def _qs2kv(qs):
Expand Down Expand Up @@ -245,4 +270,3 @@ def __exit__(self, exc_type, exc_val, exc_tb):
timeout=60,
state=flow["state"], # Optional
), indent=4))

78 changes: 78 additions & 0 deletions msal/telemetry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import uuid
import logging


logger = logging.getLogger(__name__)

CLIENT_REQUEST_ID = 'client-request-id'
CLIENT_CURRENT_TELEMETRY = "x-client-current-telemetry"
CLIENT_LAST_TELEMETRY = "x-client-last-telemetry"
NON_SILENT_CALL = 0
FORCE_REFRESH = 1
AT_ABSENT = 2
AT_EXPIRED = 3
AT_AGING = 4
RESERVED = 5


def _get_new_correlation_id():
return str(uuid.uuid4())


class _TelemetryContext(object):
"""It is used for handling the telemetry context for current OAuth2 "exchange"."""
# https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=%2FTelemetry%2FMSALServerSideTelemetry.md&_a=preview
_SUCCEEDED = "succeeded"
_FAILED = "failed"
_FAILURE_SIZE = "failure_size"
_CURRENT_HEADER_SIZE_LIMIT = 100
_LAST_HEADER_SIZE_LIMIT = 350

def __init__(self, buffer, lock, api_id, correlation_id=None, refresh_reason=None):
self._buffer = buffer
self._lock = lock
self._api_id = api_id
self._correlation_id = correlation_id or _get_new_correlation_id()
self._refresh_reason = refresh_reason or NON_SILENT_CALL
logger.debug("Generate or reuse correlation_id: %s", self._correlation_id)

def generate_headers(self):
with self._lock:
current = "4|{api_id},{cache_refresh}|".format(
api_id=self._api_id, cache_refresh=self._refresh_reason)
if len(current) > self._CURRENT_HEADER_SIZE_LIMIT:
logger.warning(
"Telemetry header greater than {} will be truncated by AAD".format(
self._CURRENT_HEADER_SIZE_LIMIT))
failures = self._buffer.get(self._FAILED, [])
return {
CLIENT_REQUEST_ID: self._correlation_id,
CLIENT_CURRENT_TELEMETRY: current,
CLIENT_LAST_TELEMETRY: "4|{succeeded}|{failed_requests}|{errors}|".format(
succeeded=self._buffer.get(self._SUCCEEDED, 0),
failed_requests=",".join("{a},{c}".format(**f) for f in failures),
errors=",".join(f["e"] for f in failures),
)
}

def hit_an_access_token(self):
with self._lock:
self._buffer[self._SUCCEEDED] = self._buffer.get(self._SUCCEEDED, 0) + 1

def update_telemetry(self, auth_result):
if auth_result:
with self._lock:
if "error" in auth_result:
self._record_failure(auth_result["error"])
else: # Telemetry sent successfully. Reset buffer
self._buffer.clear() # This won't work: self._buffer = {}

def _record_failure(self, error):
simulation = len(",{api_id},{correlation_id},{error}".format(
api_id=self._api_id, correlation_id=self._correlation_id, error=error))
if self._buffer.get(self._FAILURE_SIZE, 0) + simulation < self._LAST_HEADER_SIZE_LIMIT:
self._buffer[self._FAILURE_SIZE] = self._buffer.get(
self._FAILURE_SIZE, 0) + simulation
self._buffer.setdefault(self._FAILED, []).append({
"a": self._api_id, "c": self._correlation_id, "e": error})

2 changes: 1 addition & 1 deletion msal/token_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __add(self, event, now=None):
client_info["uid"] = id_token_claims.get("sub")
home_account_id = id_token_claims.get("sub")

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

with self._lock:
now = int(time.time() if now is None else now)
Expand Down
4 changes: 3 additions & 1 deletion msal/wstrust_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,7 @@ def parse_token_by_re(raw_response): # Returns the saml:assertion
token_types = findall_content(rstr, "TokenType")
tokens = findall_content(rstr, "RequestedSecurityToken")
if token_types and tokens:
return {"token": tokens[0].encode('us-ascii'), "type": token_types[0]}
# Historically, we use "us-ascii" encoding, but it should be "utf-8"
# https://stackoverflow.com/questions/36658000/what-is-encoding-used-for-saml-conversations
return {"token": tokens[0].encode('utf-8'), "type": token_types[0]}

3 changes: 2 additions & 1 deletion sample/username_password_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
config = json.load(open(sys.argv[1]))

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