Skip to content

Commit db4892a

Browse files
authored
Adopt new MSAL auth code flow API (Azure#16449)
1 parent 0462893 commit db4892a

File tree

7 files changed

+86
-249
lines changed

7 files changed

+86
-249
lines changed

sdk/identity/azure-identity/CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# Release History
22

33
## 1.5.1 (Unreleased)
4-
- Bumped `six` requirement from `1.6` to `1.12.0`.
4+
### Changed
5+
- Raised minimum msal version to 1.7.0
6+
- Raised minimum six version to 1.12.0
7+
8+
### Added
9+
- `InteractiveBrowserCredential` uses PKCE internally to protect authorization
10+
codes
511

612
## 1.5.0 (2020-11-11)
713
### Breaking Changes

sdk/identity/azure-identity/azure/identity/_credentials/browser.py

+10-37
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55
import socket
6-
import uuid
76
import webbrowser
87

98
from six.moves.urllib_parse import urlparse
@@ -21,14 +20,15 @@
2120

2221
if TYPE_CHECKING:
2322
# pylint:disable=unused-import
24-
from typing import Any, List, Mapping
23+
from typing import Any
2524

2625

2726
class InteractiveBrowserCredential(InteractiveCredential):
2827
"""Opens a browser to interactively authenticate a user.
2928
3029
:func:`~get_token` opens a browser to a login URL provided by Azure Active Directory and authenticates a user
31-
there with the authorization code flow. Azure Active Directory documentation describes this flow in more detail:
30+
there with the authorization code flow, using PKCE (Proof Key for Code Exchange) internally to protect the code.
31+
Azure Active Directory documentation describes the authentication flow in more detail:
3232
https://docs.microsoft.com/azure/active-directory/develop/v1-protocols-oauth-code
3333
3434
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
@@ -94,14 +94,15 @@ def _request_token(self, *scopes, **kwargs):
9494

9595
# get the url the user must visit to authenticate
9696
scopes = list(scopes) # type: ignore
97-
request_state = str(uuid.uuid4())
97+
claims = kwargs.get("claims")
9898
app = self._get_app()
99-
auth_url = app.get_authorization_request_url(
100-
scopes, redirect_uri=redirect_uri, state=request_state, prompt="select_account"
99+
flow = app.initiate_auth_code_flow(
100+
scopes, redirect_uri=redirect_uri, prompt="select_account", claims_challenge=claims
101101
)
102+
if "auth_uri" not in flow:
103+
raise CredentialUnavailableError("Failed to begin authentication flow")
102104

103-
# open browser to that url
104-
if not webbrowser.open(auth_url):
105+
if not webbrowser.open(flow["auth_uri"]):
105106
raise CredentialUnavailableError(message="Failed to open a browser")
106107

107108
# block until the server times out or receives the post-authentication redirect
@@ -112,32 +113,4 @@ def _request_token(self, *scopes, **kwargs):
112113
)
113114

114115
# redeem the authorization code for a token
115-
code = self._parse_response(request_state, response)
116-
return app.acquire_token_by_authorization_code(
117-
code, scopes=scopes, redirect_uri=redirect_uri, claims_challenge=kwargs.get("claims")
118-
)
119-
120-
@staticmethod
121-
def _parse_response(request_state, response):
122-
# type: (str, Mapping[str, Any]) -> List[str]
123-
"""Validates ``response`` and returns the authorization code it contains, if authentication succeeded.
124-
125-
Raises :class:`azure.core.exceptions.ClientAuthenticationError`, if authentication failed or ``response`` is
126-
malformed.
127-
"""
128-
129-
if "error" in response:
130-
message = "Authentication failed: {}".format(response.get("error_description") or response["error"])
131-
raise ClientAuthenticationError(message=message)
132-
if "code" not in response:
133-
# a response with no error or code is malformed; we don't know what to do with it
134-
message = "Authentication server didn't send an authorization code"
135-
raise ClientAuthenticationError(message=message)
136-
137-
# response must include the state sent in the auth request
138-
if "state" not in response:
139-
raise ClientAuthenticationError(message="Authentication response doesn't include OAuth state")
140-
if response["state"][0] != request_state:
141-
raise ClientAuthenticationError(message="Authentication response's OAuth state doesn't match the request's")
142-
143-
return response["code"]
116+
return app.acquire_token_by_auth_code_flow(flow, response, scopes=scopes, claims_challenge=claims)

sdk/identity/azure-identity/azure/identity/_internal/auth_code_redirect_handler.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def do_GET(self):
2727
return
2828

2929
query = self.path.split("?", 1)[-1]
30-
query = parse_qs(query, keep_blank_values=True)
31-
self.server.query_params = query
30+
parsed = parse_qs(query, keep_blank_values=True)
31+
self.server.query_params = {k: v[0] if isinstance(v, list) and len(v) == 1 else v for k, v in parsed.items()}
3232

3333
self.send_response(200)
3434
self.send_header("Content-Type", "text/html")

sdk/identity/azure-identity/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
install_requires=[
7575
"azure-core<2.0.0,>=1.0.0",
7676
"cryptography>=2.1.4",
77-
"msal<2.0.0,>=1.6.0",
77+
"msal<2.0.0,>=1.7.0",
7878
"msal-extensions~=0.3.0",
7979
"six>=1.12.0",
8080
],

0 commit comments

Comments
 (0)