Skip to content
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

Replace python-jose with PyJWT #227

Merged
merged 1 commit into from
Sep 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 6 additions & 5 deletions docs/optional_dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ part of your dependency on the ``globus_sdk`` package:
- ``globus_sdk[jwt]``


If you need to specify a version of ``globus_sdk`` while installing the ``jwt``
extra, simply specify it like so: ``globus_sdk[jwt]==1.0.0``


OIDC ID Tokens
--------------

Expand All @@ -22,8 +26,5 @@ The :class:`OAuthTokenResponse
conforming to the Open ID Connect specification.
If you wish to decode this token via :meth:`decode_id_token
<globus_sdk.auth.token_response.OAuthTokenResponse.decode_id_token>`, you must
install ``python-jose``, which we use to implement ID Token verification.

You may install supported versions of ``python-jose`` by install the SDK with
its ``globus_sdk[jwt]`` extra. Simply specify ``globus_sdk[jwt]`` in your
dependencies.
install ``globus_sdk[jwt]``. This extra target includes dependencies which we
use to implement ID Token verification.
16 changes: 10 additions & 6 deletions globus_sdk/auth/token_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,24 +64,28 @@ def decode_id_token(self, auth_client):
"""
logger.info('Decoding ID Token "{}"'.format(self['id_token']))
try:
from jose import jwt
import jwt
except ImportError:
logger.error('OptionalDependencyError(python-jose)')
logger.error('OptionalDependencyError(jwt)')
raise GlobusOptionalDependencyError(
["python-jose or globus_sdk[jwt]"],
["globus_sdk[jwt]"],
"JWT Parsing via OAuthTokenResponse.id_token")

logger.debug('Fetch JWK Data: Start')
oidc_conf = auth_client.get('/.well-known/openid-configuration')
jwks_uri = oidc_conf['jwks_uri']
signing_algos = oidc_conf['id_token_signing_alg_values_supported']

# use the auth_client's decision on ssl_verify=yes/no
jwk_data = requests.get(jwks_uri, verify=auth_client._verify).json()
logger.debug('Fetch JWK Data: Complete')
# decode from JWK to an RSA PEM key for JWT decoding
jwk_as_pem = jwt.algorithms.RSAAlgorithm.from_jwk(
json.dumps(jwk_data['keys'][0]))

return jwt.decode(
self['id_token'], jwk_data,
access_token=self['access_token'],
audience=auth_client.client_id)
self['id_token'], jwk_as_pem,
algorithms=signing_algos, audience=auth_client.client_id)

def __str__(self):
# Make printing responses more convenient by only showing the
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
],

extras_require={
'jwt': ['python-jose>=1.3.0,<2.0.0']
'jwt': ['pyjwt[crypto]>=1.5.3,<2.0.0']
},

include_package_data=True,
Expand Down
Empty file.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import time
import unittest
try:
import jose
JOSE_FLAG = True
import jwt
JWT_FLAG = True
except ImportError:
JOSE_FLAG = False
JWT_FLAG = False
try:
import mock
except ImportError:
Expand Down Expand Up @@ -57,8 +57,9 @@ def setUp(self):
self.ac.client_id = get_client_data()["native_app_client1"]["id"]
self.ac._verify = True
self.ac.get = mock.Mock(return_value={
"jwks_uri":
u"https://auth.globus.org/jwk.json"})
"jwks_uri": "https://auth.globus.org/jwk.json",
"id_token_signing_alg_values_supported": ["RS512"]
})

def test_convert_token_info_dict(self):
"""
Expand Down Expand Up @@ -118,15 +119,15 @@ def test_by_resource_server(self):
self.assertIn(server_data["expires_at_seconds"],
(expected - 1, expected, expected + 1))

@unittest.skipIf(JOSE_FLAG, "python-jose successfully imported")
def test_decode_id_token_no_jose(self):
@unittest.skipIf(JWT_FLAG, "pyjwt successfully imported")
def test_decode_id_token_no_jwt(self):
"""
If jose was not imported, confirms OptionalDependencyError
If pyjwt was not imported, confirms OptionalDependencyError
"""
with self.assertRaises(GlobusOptionalDependencyError):
self.response.decode_id_token(self.ac)

@unittest.skipIf(not JOSE_FLAG, "python-jose not imported")
@unittest.skipIf(not JWT_FLAG, "pyjwt not imported")
def test_decode_id_token_invalid_id(self):
"""
Creates a response with an invalid id_token, and attempts to decode
Expand All @@ -137,16 +138,16 @@ def test_decode_id_token_invalid_id(self):
http_response.headers["Content-Type"] = "application/json"
id_response = OAuthTokenResponse(http_response)

with self.assertRaises(jose.exceptions.JWTError):
with self.assertRaises(jwt.exceptions.InvalidTokenError):
id_response.decode_id_token(self.ac)

@unittest.skipIf(not JOSE_FLAG, "python-jose not imported")
@unittest.skipIf(not JWT_FLAG, "pyjwt not imported")
def test_decode_id_token_expired(self):
"""
Attempt to decode an expired id_token, confirms that the token is
decoded, but errors out on the expired signature.
"""
with self.assertRaises(jose.exceptions.ExpiredSignatureError):
with self.assertRaises(jwt.exceptions.ExpiredSignatureError):
self.response.decode_id_token(self.ac)


Expand Down