def test_encode_decode_with_ecdsa_sha512(self): try: import ecdsa # PEM-formatted EC key with open('tests/testkey_ec', 'r') as ec_priv_file: priv_eckey = ecdsa.SigningKey.from_pem(ec_priv_file.read()) jwt_message = jwt.encode(self.payload, priv_eckey, algorithm='ES512') with open('tests/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ecdsa.VerifyingKey.from_pem(ec_pub_file.read()) assert jwt.decode(jwt_message, pub_eckey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_eckey, *load_output) # string-formatted key with open('tests/testkey_ec', 'r') as ec_priv_file: priv_eckey = ec_priv_file.read() jwt_message = jwt.encode(self.payload, priv_eckey, algorithm='ES512') with open('tests/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ec_pub_file.read() assert jwt.decode(jwt_message, pub_eckey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_eckey, *load_output) except ImportError: pass
def test_encode_decode_with_rsa_sha512(self): try: from Crypto.PublicKey import RSA # RSA-formatted key with open('tests/testkey', 'r') as rsa_priv_file: priv_rsakey = RSA.importKey(rsa_priv_file.read()) jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512') with open('tests/testkey.pub', 'r') as rsa_pub_file: pub_rsakey = RSA.importKey(rsa_pub_file.read()) assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) # string-formatted key with open('tests/testkey', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512') with open('tests/testkey.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) except ImportError: pass
def test_encode_decode_with_rsa_sha256(self): # PEM-formatted RSA key with open('tests/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = load_pem_private_key(ensure_bytes(rsa_priv_file.read()), password=None, backend=default_backend()) jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS256') with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = load_ssh_public_key(ensure_bytes(rsa_pub_file.read()), backend=default_backend()) assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) # string-formatted key with open('tests/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS256') with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output)
def test_encode_decode_with_rsa_sha512(self): try: from Crypto.PublicKey import RSA # PEM-formatted RSA key with open('tests/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = RSA.importKey(rsa_priv_file.read()) jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512') with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = RSA.importKey(rsa_pub_file.read()) assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) # string-formatted key with open('tests/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512') with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) except ImportError: pass
def test_encode_decode_with_ecdsa_sha512(self): # PEM-formatted EC key with open('tests/testkey_ec', 'r') as ec_priv_file: priv_eckey = load_pem_private_key(ensure_bytes(ec_priv_file.read()), password=None, backend=default_backend()) jwt_message = jwt.encode(self.payload, priv_eckey, algorithm='ES512') with open('tests/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = load_pem_public_key(ensure_bytes(ec_pub_file.read()), backend=default_backend()) assert jwt.decode(jwt_message, pub_eckey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_eckey, *load_output) # string-formatted key with open('tests/testkey_ec', 'r') as ec_priv_file: priv_eckey = ec_priv_file.read() jwt_message = jwt.encode(self.payload, priv_eckey, algorithm='ES512') with open('tests/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ec_pub_file.read() assert jwt.decode(jwt_message, pub_eckey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_eckey, *load_output)
def parse_token(token): """Parses a JWT OAuth 2.0 Access Token into a joat.Token object. Raises an ExpiredSignature exception if the token is expired. Returns None in other cases of invalidity.""" try: claims, enc, header, sig = jwt.load(token) salt = salt_generator(claims) verified_claims = jwt.decode(token, salt) except jwt.DecodeError as e: # improperly formatted token return None except jwt.ExpiredSignature as e: # token is expired, return none return None payload = { 'provider': verified_claims['iss'], 'client_id': verified_claims['aud'], 'user_id': verified_claims['sub'], 'authorized_scope': verified_claims['scp'] } if 'jti' in verified_claims: payload['jti'] = verified_claims['jti'] return payload
def test_custom_headers(self): right_secret = 'foo' headers = {'foo': 'bar', 'kid': 'test'} jwt_message = jwt.encode(self.payload, right_secret, headers=headers) decoded_payload, signing, header, signature = jwt.load(jwt_message) for key, value in headers.items(): self.assertEqual(header[key], value)
def test_verify_signature_no_secret(self): right_secret = 'foo' jwt_message = jwt.encode(self.payload, right_secret) decoded_payload, signing, header, signature = jwt.load(jwt_message) self.assertRaises( jwt.DecodeError, lambda: jwt.verify_signature( decoded_payload, signing, header, signature))
def test_verify_signature_no_secret(self): right_secret = 'foo' jwt_message = jwt.encode(self.payload, right_secret) decoded_payload, signing, header, signature = jwt.load(jwt_message) self.assertRaises( jwt.DecodeError, lambda: jwt.verify_signature(decoded_payload, signing, header, signature))
def test_decode_skip_expiration_verification(self): self.payload['exp'] = time.time() - 1 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) jwt.decode(jwt_message, secret, verify_expiration=False) decoded_payload, signing, header, signature = jwt.load(jwt_message) jwt.verify_signature(decoded_payload, signing, header, signature, secret, verify_expiration=False)
def test_decode_invalid_header_string(self): example_jwt = ("eyJhbGciOiAiSFMyNTbpIiwgInR5cCI6ICJKV1QifQ==" ".eyJoZWxsbyI6ICJ3b3JsZCJ9" ".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") example_secret = "secret" try: jwt.load(example_jwt) except jwt.DecodeError as e: self.assertTrue('Invalid header string' in str(e)) else: self.fail('DecodeError not raised') try: jwt.decode(example_jwt, example_secret) except jwt.DecodeError as e: self.assertTrue('Invalid header string' in str(e)) else: self.fail('DecodeError not raised')
def test_decode_invalid_crypto_padding(self): example_jwt = ("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" ".eyJoZWxsbyI6ICJ3b3JsZCJ9" ".aatvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") example_secret = "secret" self.assertRaises(jwt.DecodeError, lambda: jwt.load(example_jwt)) self.assertRaises(jwt.DecodeError, lambda: jwt.decode(example_jwt, example_secret))
def test_decode_skip_notbefore_verification(self): self.payload['nbf'] = time.time() + 10 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) jwt.decode(jwt_message, secret, verify_expiration=False) decoded_payload, signing, header, signature = jwt.load(jwt_message) jwt.verify_signature(decoded_payload, signing, header, signature, secret, verify_expiration=False)
def test_decode_invalid_payload_string(self): example_jwt = ("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" ".eyJoZWxsb-kiOiAid29ybGQifQ==" ".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") example_secret = "secret" try: jwt.load(example_jwt) except jwt.DecodeError as e: self.assertTrue('Invalid payload string' in str(e)) else: self.fail('DecodeError not raised') try: jwt.decode(example_jwt, example_secret) except jwt.DecodeError as e: self.assertTrue('Invalid payload string' in str(e)) else: self.fail('DecodeError not raised')
def test_decode_unicode_value(self): example_payload = {"hello": "world"} example_secret = "secret" example_jwt = ("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" ".eyJoZWxsbyI6ICJ3b3JsZCJ9" ".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") decoded_payload = jwt.decode(example_jwt, example_secret) self.assertEqual(decoded_payload, example_payload) decoded_payload, signing, header, signature = jwt.load(example_jwt) self.assertEqual(decoded_payload, example_payload)
def test_decode_invalid_payload_string(self): example_jwt = ( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsb-kiOiAid29ybGQifQ==' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' try: jwt.load(example_jwt) except jwt.DecodeError as e: self.assertTrue('Invalid payload string' in str(e)) else: self.fail('DecodeError not raised') try: jwt.decode(example_jwt, example_secret) except jwt.DecodeError as e: self.assertTrue('Invalid payload string' in str(e)) else: self.fail('DecodeError not raised')
def test_decode_invalid_header_string(self): example_jwt = ( 'eyJhbGciOiAiSFMyNTbpIiwgInR5cCI6ICJKV1QifQ==' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' try: jwt.load(example_jwt) except jwt.DecodeError as e: self.assertTrue('Invalid header string' in str(e)) else: self.fail('DecodeError not raised') try: jwt.decode(example_jwt, example_secret) except jwt.DecodeError as e: self.assertTrue('Invalid header string' in str(e)) else: self.fail('DecodeError not raised')
def test_decode_unicode_value(self): example_payload = {'hello': 'world'} example_secret = 'secret' example_jwt = ( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') decoded_payload = jwt.decode(example_jwt, example_secret) self.assertEqual(decoded_payload, example_payload) decoded_payload, signing, header, signature = jwt.load(example_jwt) self.assertEqual(decoded_payload, example_payload)
def test_decode_with_expiration(self): self.payload['exp'] = utc_timestamp() - 1 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) self.assertRaises(jwt.ExpiredSignature, lambda: jwt.decode(jwt_message, secret)) decoded_payload, signing, header, signature = jwt.load(jwt_message) self.assertRaises( jwt.ExpiredSignature, lambda: jwt.verify_signature( decoded_payload, signing, header, signature, secret))
def test_load_verify_valid_jwt(self): example_payload = {'hello': 'world'} example_secret = 'secret' example_jwt = ( b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' b'.eyJoZWxsbyI6ICJ3b3JsZCJ9' b'.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') decoded_payload, signing, header, signature = jwt.load(example_jwt) jwt.verify_signature(decoded_payload, signing, header, signature, example_secret) self.assertEqual(decoded_payload, example_payload)
def test_unicode_secret(self): secret = '\xc2' jwt_message = jwt.encode(self.payload, secret) decoded_payload = jwt.decode(jwt_message, secret) self.assertEqual(decoded_payload, self.payload) decoded_payload, signing, header, signature = jwt.load(jwt_message) jwt.verify_signature(decoded_payload, signing, header, signature, secret) self.assertEqual(decoded_payload, self.payload)
def test_bytes_secret(self): secret = b'\xc2' # char value that ascii codec cannot decode jwt_message = jwt.encode(self.payload, secret) decoded_payload = jwt.decode(jwt_message, secret) self.assertEqual(decoded_payload, self.payload) decoded_payload, signing, header, signature = jwt.load(jwt_message) jwt.verify_signature(decoded_payload, signing, header, signature, secret) self.assertEqual(decoded_payload, self.payload)
def test_load_verify_valid_jwt(self): example_payload = {"hello": "world"} example_secret = "secret" example_jwt = ( b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" b".eyJoZWxsbyI6ICJ3b3JsZCJ9" b".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") decoded_payload, signing, header, signature = jwt.load(example_jwt) jwt.verify_signature(decoded_payload, signing, header, signature, example_secret) self.assertEqual(decoded_payload, example_payload)
def test_decode_invalid_header_padding(self): example_jwt = ( "aeyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" ".eyJoZWxsbyI6ICJ3b3JsZCJ9" ".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8") example_secret = "secret" self.assertRaises( jwt.DecodeError, lambda: jwt.load(example_jwt)) self.assertRaises( jwt.DecodeError, lambda: jwt.decode(example_jwt, example_secret))
def test_decode_invalid_header_padding(self): example_jwt = ( 'aeyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' self.assertRaises( jwt.DecodeError, lambda: jwt.load(example_jwt)) self.assertRaises( jwt.DecodeError, lambda: jwt.decode(example_jwt, example_secret))
def test_decode_invalid_crypto_padding(self): example_jwt = ( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.aatvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' self.assertRaises( jwt.DecodeError, lambda: jwt.load(example_jwt)) self.assertRaises( jwt.DecodeError, lambda: jwt.decode(example_jwt, example_secret))
def test_decode_with_notbefore(self): self.payload['nbf'] = utc_timestamp() + 10 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) self.assertRaises( jwt.ExpiredSignature, lambda: jwt.decode(jwt_message, secret)) decoded_payload, signing, header, signature = jwt.load(jwt_message) self.assertRaises( jwt.ExpiredSignature, lambda: jwt.verify_signature( decoded_payload, signing, header, signature, secret))
def remote_user_jwt(request): # type: (HttpRequest) -> HttpResponse try: json_web_token = request.POST["json_web_token"] payload, signing_input, header, signature = jwt.load(json_web_token) except KeyError: raise JsonableError(_("No JSON web token passed in request")) except jwt.DecodeError: raise JsonableError(_("Bad JSON web token")) remote_user = payload.get("user", None) if remote_user is None: raise JsonableError(_("No user specified in JSON web token claims")) domain = payload.get('realm', None) if domain is None: raise JsonableError(_("No domain specified in JSON web token claims")) email = "%s@%s" % (remote_user, domain) try: jwt.verify_signature(payload, signing_input, header, signature, settings.JWT_AUTH_KEYS[domain]) # We do all the authentication we need here (otherwise we'd have to # duplicate work), but we need to call authenticate with some backend so # that the request.backend attribute gets set. return_data = {} # type: Dict[str, bool] user_profile = authenticate(username=email, realm_subdomain=get_subdomain(request), return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): logging.warning( "User attempted to JWT login to wrong subdomain %s: %s" % ( get_subdomain(request), email, )) raise JsonableError(_("Wrong subdomain")) except (jwt.DecodeError, jwt.ExpiredSignature): raise JsonableError(_("Bad JSON web token signature")) except KeyError: raise JsonableError(_("Realm not authorized for JWT login")) except UserProfile.DoesNotExist: user_profile = None return login_or_register_remote_user(request, email, user_profile, remote_user)
def test_encode_decode_with_rsa_sha384(self): try: from Crypto.PublicKey import RSA with open('tests/testkey', 'r') as rsa_priv_file: priv_rsakey = RSA.importKey(rsa_priv_file.read()) jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS384') with open('tests/testkey.pub', 'r') as rsa_pub_file: pub_rsakey = RSA.importKey(rsa_pub_file.read()) assert jwt.decode(jwt_message, pub_rsakey) load_output = jwt.load(jwt_message) jwt.verify_signature(key=pub_rsakey, *load_output) except ImportError: pass
def remote_user_jwt(request): # type: (HttpRequest) -> HttpResponse try: json_web_token = request.POST["json_web_token"] payload, signing_input, header, signature = jwt.load(json_web_token) except KeyError: raise JsonableError(_("No JSON web token passed in request")) except jwt.DecodeError: raise JsonableError(_("Bad JSON web token")) remote_user = payload.get("user", None) if remote_user is None: raise JsonableError(_("No user specified in JSON web token claims")) domain = payload.get('realm', None) if domain is None: raise JsonableError(_("No domain specified in JSON web token claims")) email = "%s@%s" % (remote_user, domain) try: jwt.verify_signature(payload, signing_input, header, signature, settings.JWT_AUTH_KEYS[domain]) # We do all the authentication we need here (otherwise we'd have to # duplicate work), but we need to call authenticate with some backend so # that the request.backend attribute gets set. return_data = {} # type: Dict[str, bool] user_profile = authenticate(username=email, realm_subdomain=get_subdomain(request), return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): logging.warning("User attempted to JWT login to wrong subdomain %s: %s" % (get_subdomain(request), email,)) raise JsonableError(_("Wrong subdomain")) except (jwt.DecodeError, jwt.ExpiredSignature): raise JsonableError(_("Bad JSON web token signature")) except KeyError: raise JsonableError(_("Realm not authorized for JWT login")) except UserProfile.DoesNotExist: user_profile = None return login_or_register_remote_user(request, email, user_profile, remote_user)
def test_decode_with_notbefore_with_leeway(self): self.payload['nbf'] = utc_timestamp() + 10 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) decoded_payload, signing, header, signature = jwt.load(jwt_message) # With 13 seconds leeway, should be ok jwt.decode(jwt_message, secret, leeway=13) jwt.verify_signature(decoded_payload, signing, header, signature, secret, leeway=13) # With 1 seconds, should fail self.assertRaises( jwt.ExpiredSignature, lambda: jwt.decode(jwt_message, secret, leeway=1)) self.assertRaises( jwt.ExpiredSignature, lambda: jwt.verify_signature(decoded_payload, signing, header, signature, secret, leeway=1))
def test_decode_with_expiration_with_leeway(self): self.payload['exp'] = utc_timestamp() - 2 secret = 'secret' jwt_message = jwt.encode(self.payload, secret) decoded_payload, signing, header, signature = jwt.load(jwt_message) # With 3 seconds leeway, should be ok jwt.decode(jwt_message, secret, leeway=3) jwt.verify_signature(decoded_payload, signing, header, signature, secret, leeway=3) # With 1 seconds, should fail self.assertRaises( jwt.ExpiredSignature, lambda: jwt.decode(jwt_message, secret, leeway=1)) self.assertRaises( jwt.ExpiredSignature, lambda: jwt.verify_signature(decoded_payload, signing, header, signature, secret, leeway=1))
def test_load_no_verification(self): right_secret = 'foo' jwt_message = jwt.encode(self.payload, right_secret) decoded_payload, signing, header, signature = jwt.load(jwt_message) self.assertEqual(decoded_payload, self.payload)
def authorized_handler(self): if ('state' in request.args or 'oauth_random' in session) and \ request.args['state'] != hashlib.sha256( current_app.secret_key + session.pop('oauth_random', '')).hexdigest(): session['openid_error'] = 'Access denied: bad state' return redirect(self.get_current_url()) resp = self.app.authorized_response() if resp is None: # XXX Hack to minimze changes session['openid_error'] = ('Access denied: %s %s' % (request.args['error_reason'], request.args['error_description'])) return redirect(self.get_current_url()) if 'oauth_token' in resp: session['%s_oauth_token' % self.provider] = (resp['oauth_token'], resp['oauth_token_secret']) else: session['%s_oauth_token' % self.provider] = (resp['access_token'], '') if 'screen_name' in resp: return self.after_login_func( AuthResponse(self.provider, resp['user_id'], nickname=resp.get('screen_name'))) sub = None if self.jwks_uri: try: _, _, header, _ = jwt.load(resp['id_token']) except AttributeError: header = jwt.get_unverified_header(resp['id_token']) kid = header.get('kid') if kid not in self.jwks: h = httplib2.Http() hdr, content = h.request(self.jwks_uri) self.jwks = {} for key in json.loads(content.decode('utf-8'))['keys']: self.jwks[key.get('kid')] = key if kid: keys = [self.jwks[kid]] else: keys = self.jwks.values() data = None for key in keys: try: _key = self._json_to_key(key) try: data = jwt.decode(resp['id_token'], _key) except TypeError: data = jwt.decode(resp['id_token'], _key.exportKey(format='PEM'), audience=self.app.consumer_key) except jwt.DecodeError: pass else: break if not data: session['openid_error'] = 'Access denied' return redirect(self.get_current_url()) sub = data['sub'] # Facebook and Windows Live needs another call to fetch user id data = None me = self.app.get(self.userinfo_endpoint, data=data) email = None if 'email' in me.data: email = me.data['email'] elif 'emails' in me.data: email = me.data['emails'].get('preferred') if 'sub' in me.data: if sub and sub != me.data['sub']: raise RuntimeError('Authentication error!') sub = me.data['sub'] else: sub = me.data['id'] return self.after_login_func( AuthResponse(self.provider, sub, fullname=me.data.get('name'), email=email))
def identify(mw_uri, consumer_token, access_token, leeway=10.0): """ Gather's identifying information about a user via an authorized token. :Parameters: mw_uri : `str` The base URI of the MediaWiki installation. Note that the URI should end in ``"index.php"``. consumer_token : :class:`~mwoauth.ConsumerToken` A token representing you, the consumer. access_token : :class:`~mwoauth.AccessToken` A token representing an authorized user. Obtained from `complete()` leeway : `int` | `float` The number of seconds of leeway to account for when examining a tokens "issued at" timestamp. :Returns: A dictionary containing identity information. """ # Construct an OAuth auth auth = OAuth1(consumer_token.key, client_secret=consumer_token.secret, resource_owner_key=access_token.key, resource_owner_secret=access_token.secret) # Request the identity using auth r = requests.post(url=mw_uri, params={'title': "Special:OAuth/identify"}, auth=auth) # Decode json & stuff try: identity, signing_input, header, signature = jwt.load(r.content) except jwt.DecodeError as e: raise Exception("An error occurred while trying to read json " + "content: {0}".format(e)) # Ensure no downgrade in authentication if not header['alg'] == "HS256": raise Exception("Unexpected algorithm used for authentication " + "{0}, expected {1}".format("HS256", header['alg'])) # Check signature try: jwt.verify_signature(identity, signing_input, header, signature, consumer_token.secret, False) except jwt.DecodeError as e: raise Exception("Could not verify the jwt signature: {0}".format(e)) # Verify the issuer is who we expect (server sends $wgCanonicalServer) issuer = urlparse(identity['iss']).netloc expected_domain = urlparse(mw_uri).netloc if not issuer == expected_domain: raise Exception("Unexpected issuer " + "{0}, expected {1}".format(issuer, expected_domain)) # Verify we are the intended audience of this response audience = identity['aud'] if not audience == consumer_token.key: raise Exception("Unexpected audience " + "{0}, expected {1}".format(audience, expected_domain)) now = time.time() # Check that the identity was issued in the past. issued_at = float(identity['iat']) if not now >= (issued_at - leeway): raise Exception("Identity issued {0} ".format(issued_at - now) + "seconds in the future!") # Check that the identity has not yet expired expiration = float(identity['exp']) if not now <= expiration: raise Exception("Identity expired {0} ".format(expiration - now) + "seconds ago!") # Verify that the nonce matches our request one, # to avoid a replay attack authorization_header = force_unicode(r.request.headers['Authorization']) request_nonce = re.search(r'oauth_nonce="(.*?)"', authorization_header).group(1) if identity['nonce'] != request_nonce: raise Exception('Replay attack detected: {0} != {1}'.format( identity['nonce'], request_nonce)) return identity