diff --git a/msal/oauth2cli/assertion.py b/msal/oauth2cli/assertion.py index f8d3f16f..0cf58799 100644 --- a/msal/oauth2cli/assertion.py +++ b/msal/oauth2cli/assertion.py @@ -9,6 +9,15 @@ logger = logging.getLogger(__name__) + +def _str2bytes(raw): + # A conversion based on duck-typing rather than six.text_type + try: # Assuming it is a string + return raw.encode(encoding="utf-8") + except: # Otherwise we treat it as bytes and return it as-is + return raw + + class AssertionCreator(object): def create_normal_assertion( self, audience, issuer, subject, expires_at=None, expires_in=600, @@ -103,8 +112,9 @@ def create_normal_assertion( payload['nbf'] = not_before payload.update(additional_claims or {}) try: - return jwt.encode( + str_or_bytes = jwt.encode( # PyJWT 1 returns bytes, PyJWT 2 returns str payload, self.key, algorithm=self.algorithm, headers=self.headers) + return _str2bytes(str_or_bytes) # We normalize them into bytes except: if self.algorithm.startswith("RS") or self.algorithm.starswith("ES"): logger.exception( diff --git a/msal/oauth2cli/oauth2.py b/msal/oauth2cli/oauth2.py index 58b2dbb0..9e30e7f4 100644 --- a/msal/oauth2cli/oauth2.py +++ b/msal/oauth2cli/oauth2.py @@ -99,8 +99,8 @@ def __init__( client_secret (str): Triggers HTTP AUTH for Confidential Client client_assertion (bytes, callable): The client assertion to authenticate this client, per RFC 7521. - It can be a raw SAML2 assertion (this method will encode it for you), - or a raw JWT assertion. + It can be a raw SAML2 assertion (we will base64 encode it for you), + or a raw JWT assertion in bytes (which we will relay to http layer). It can also be a callable (recommended), so that we will do lazy creation of an assertion. client_assertion_type (str): @@ -198,7 +198,9 @@ def _obtain_token( # The verb "obtain" is influenced by OAUTH2 RFC 6749 self.default_body["client_assertion_type"], lambda a: a) _data["client_assertion"] = encoder( self.client_assertion() # Do lazy on-the-fly computation - if callable(self.client_assertion) else self.client_assertion) + if callable(self.client_assertion) else self.client_assertion + ) # The type is bytes, which is preferrable. See also: + # https://github.com/psf/requests/issues/4503#issuecomment-455001070 _data.update(self.default_body) # It may contain authen parameters _data.update(data or {}) # So the content in data param prevails diff --git a/setup.py b/setup.py index 51c988dd..0555c667 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ # See https://stackoverflow.com/a/14211600/728675 for more detail install_requires=[ 'requests>=2.0.0,<3', - 'PyJWT[crypto]>=1.0.0,<2', + 'PyJWT[crypto]>=1.0.0,<3', 'cryptography>=0.6,<4', # load_pem_private_key() is available since 0.6