def load_key(self, jwks_endpoint): """ A custom method to load a Synapse "RS256" key. Synapse is not providing standard JWK keys: * kty is RS256 not RSA * e and n are not base64-encoded Synapse is updating their JWKS document to align it with conventions, so above logic could be abandoned in the future. """ for key in self.get_jwt_keys(jwks_endpoint): # For new Synapse JWKS doc, which is modified with conventions if key["kty"] == "RSA": return "RS256", RSAAlgorithm.from_jwk(json.dumps(key)) # For old Synapse JWKS odc, kept for backward compability # TODO: remove after tested with new Synapse JWKS doc # and Synapse has deployed their changes elif key["kty"] == "RS256": key["kty"] = "RSA" for field in ["e", "n"]: if key[field].isdigit(): key[field] = to_base64url_uint(int(key[field])).decode() return "RS256", RSAAlgorithm.from_jwk(json.dumps(key)) return None, None
def test_rsa_jwk_private_key_with_missing_required_values_is_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path("jwk_rsa_key.json")) as keyfile: with pytest.raises(InvalidKeyError): keydata = json.loads(keyfile.read()) del keydata["p"] algo.from_jwk(json.dumps(keydata))
def test_rsa_jwk_private_key_with_other_primes_is_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path("jwk_rsa_key.json")) as keyfile: with pytest.raises(InvalidKeyError): keydata = json.loads(keyfile.read()) keydata["oth"] = [] algo.from_jwk(json.dumps(keydata))
def test_rsa_jwk_raises_exception_if_not_a_valid_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) # Invalid JSON with pytest.raises(InvalidKeyError): algo.from_jwk("{not-a-real-key") # Missing key parts with pytest.raises(InvalidKeyError): algo.from_jwk('{"kty": "RSA"}')
def test_rsa_jwk_public_and_private_keys_should_parse_and_verify(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path("jwk_rsa_pub.json")) as keyfile: pub_key = algo.from_jwk(keyfile.read()) with open(key_path("jwk_rsa_key.json")) as keyfile: priv_key = algo.from_jwk(keyfile.read()) signature = algo.sign(b"Hello World!", priv_key) assert algo.verify(b"Hello World!", pub_key, signature)
def get_oidc_configuration(app): OIDC_ISSUER_URL_BY_PROVIDER = { 'azure': 'https://sts.windows.net/{}/'.format(app.config['AZURE_TENANT']), 'gitlab': app.config['GITLAB_URL'], 'google': 'https://accounts.google.com', 'keycloak': '{}/auth/realms/{}'.format(app.config['KEYCLOAK_URL'], app.config['KEYCLOAK_REALM']) } issuer_url = OIDC_ISSUER_URL_BY_PROVIDER.get(app.config['AUTH_PROVIDER']) or app.config['OIDC_ISSUER_URL'] if not issuer_url: raise ApiError('Must define Issuer URL (OIDC_ISSUER_URL) in server configuration to use OpenID Connect.', 503) discovery_doc_url = issuer_url.strip('/') + '/.well-known/openid-configuration' try: r = requests.get(discovery_doc_url) config = r.json() except Exception as e: raise ApiError('Could not get OpenID configuration from well known URL: {}'.format(str(e)), 503) if config['issuer'] != issuer_url: raise ApiError('Issuer Claim does not match Issuer URL used to retrieve OpenID configuration', 503) if app.config['OIDC_VERIFY_TOKEN']: try: jwks_uri = config['jwks_uri'] r = requests.get(jwks_uri) keys = {k['kid']: RSAAlgorithm.from_jwk(json.dumps(k)) for k in r.json()['keys']} except Exception as e: raise ApiError('Could not get OpenID JWT Key Set from JWKS URL: {}'.format(str(e)), 503) else: keys = {} return config, keys
def decorated(*args, **kwargs): token = get_token_auth_header(request) with urllib.request.urlopen(os.getenv('AUTH0_BASE_URL') + "/.well-known/jwks.json") as url: jwks = json.loads(url.read().decode()) unverified_header = jwt.get_unverified_header(token) rsa_key = {} for key in jwks["keys"]: if key["kid"] == unverified_header["kid"]: rsa_key = RSAAlgorithm.from_jwk(json.dumps(key)) if rsa_key: try: payload = jwt.decode( token, rsa_key, algorithms=ALGORITHMS, audience=os.getenv('AUTH0_WEB_API_AUDIENCE'), issuer=os.getenv('AUTH0_BASE_URL') + "/" ) except jwt.ExpiredSignatureError: raise CoreAuthError({"code": "token_expired", "description": "token is expired"}, 401) except jwt.MissingRequiredClaimError: raise CoreAuthError({"code": "invalid_claims", "description": "incorrect claims," "please check the audience and issuer"}, 401) except Exception as e: raise CoreAuthError({"code": "invalid_header", "description": str(e)}, 401) _request_ctx_stack.top.current_user = payload return f(*args, **kwargs) raise CoreAuthError({"code": "invalid_header", "description": "Unable to find appropriate key"}, 401)
def get_current_user(self): if isinstance(self, UI): return True if sickbeard.WEB_USERNAME and sickbeard.WEB_PASSWORD: # Authenticate using jwt for CF Access # NOTE: Setting a username and password is STILL required to protect poorly configured tunnels or firewalls if sickbeard.CF_AUTH_DOMAIN and sickbeard.CF_POLICY_AUD and has_cryptography: CERTS_URL = "{}/cdn-cgi/access/certs".format( sickbeard.CF_AUTH_DOMAIN) if 'CF_Authorization' in self.request.cookies: jwk_set = helpers.getURL(CERTS_URL, returns='json') for key_dict in jwk_set['keys']: public_key = jwt_algorithms_RSAAlgorithm.from_jwk( json.dumps(key_dict)) if jwt.decode(self.request.cookies['CF_Authorization'], key=public_key, audience=sickbeard.CF_POLICY_AUD): return True # Basic Auth at a minimum auth_header = self.request.headers.get('Authorization') if auth_header and auth_header.startswith('Basic '): auth_decoded = base64.decodestring(auth_header[6:]) username, password = auth_decoded.split(':', 2) if username == sickbeard.WEB_USERNAME and password == sickbeard.WEB_PASSWORD: return True return False # Logged into UI? return self.get_secure_cookie('sickchill_user') else: # Local network return helpers.is_ip_private(self.request.remote_ip)
def get_oidc_configuration(app): OIDC_ISSUER_URL_BY_PROVIDER = { 'azure': 'https://sts.windows.net/{}/'.format(app.config['AZURE_TENANT']), 'gitlab': app.config['GITLAB_URL'], 'google': 'https://accounts.google.com', 'keycloak': '{}/auth/realms/{}'.format(app.config['KEYCLOAK_URL'], app.config['KEYCLOAK_REALM']) } issuer_url = OIDC_ISSUER_URL_BY_PROVIDER.get(app.config['AUTH_PROVIDER']) or app.config['OIDC_ISSUER_URL'] if not issuer_url: raise ApiError('Must define Issuer URL (OIDC_ISSUER_URL) in server configuration to use OpenID Connect.', 503) discovery_doc_url = issuer_url.strip('/') + '/.well-known/openid-configuration' r = requests.get(discovery_doc_url) config = r.json() if config['issuer'] != issuer_url: raise ApiError('Issuer Claim does not match Issuer URL used to retrieve OpenID configuration', 503) jwks_uri = config['jwks_uri'] r = requests.get(jwks_uri) keys = {k['kid']: RSAAlgorithm.from_jwk(json.dumps(k)) for k in r.json()['keys']} return config, keys
def retrieve_keycloak_public_key_and_algorithm(token_kid: str, oidc_server_url: str) -> (str, str): """ Retrieve the public key for the token from keycloak :param token_kid: The user token :param oidc_server_url: Url of the server to authorize with :return: keycloak public key and algorithm """ handle = f'{oidc_server_url}/certs' logger.info(f'Getting public key for the kid={token_kid} from the keycloak...') r = requests.get(handle) if r.status_code != 200: error = "Could not get certificates from the keycloak. " \ "Reason: [{}]: {}".format(r.status_code, r.text) logger.error(error) raise ValueError(error) try: json_response = r.json() except Exception: error = "Could not retrieve the public key. " \ "Got unexpected response: '{}'".format(r.text) logger.error(error) raise ValueError(error) try: matching_key = next((item for item in json_response.get('keys') if item['kid'] == token_kid), None) matching_key_json = json.dumps(matching_key) public_key = RSAAlgorithm.from_jwk(matching_key_json) except Exception as e: error = f'Invalid public key!. Reason: {e}' logger.error(error) raise ValueError(error) logger.info(f'The public key for the kid={token_kid} has been fetched.') return matching_key.get('alg'), public_key
def decode_jwt(encoded_token): """ Returns the decoded token from an encoded one. This does all the checks to insure that the decoded token is valid before returning it. """ audience = current_app.config['JWT_AUDIENCE'] required_scopes = current_app.config['JWT_REQUIRED_SCOPES'] header = jwt.get_unverified_header(encoded_token) kid = header["kid"] keys = get_jwks()["keys"] keys = {key["kid"]: key for key in keys} if kid not in keys: msg = "No key matching kid: {} found".format(kid) raise NoKeyMatchingKidFound(msg) public_key = RSAAlgorithm.from_jwk(json.dumps(keys[kid])) decoded = jwt.decode(encoded_token, key=public_key, algorithms=['RS256'], audience=audience) if len(required_scopes) > 0: if "scopes" not in decoded: msg = "Missing required scopes: {}".format( pformat(required_scopes)) raise MissingRequiredScopeError(msg) else: scopes = decoded["scopes"] for required_scope in required_scopes: if required_scope not in scopes: msg = "Missing required scope: {}".format( pformat(required_scope)) raise MissingRequiredScopeError(msg) return decoded
def _get_public_keys(): response = requests.get(KEYS_URL) key_list = json.loads(response.text).get("keys", []) return { key["kid"]: RSAAlgorithm.from_jwk(json.dumps(key)) for key in key_list }
def _get_keys(self): """Assemble a list of valid signing public keys we use to verify the token""" decoded_keys = {} # We have a test key loaded if settings.KEYCLOAK['RS256_KEY'] is not None: decoded_keys['imported'] = settings.KEYCLOAK['RS256_KEY'] if not settings.KEYCLOAK['DOWNLOAD_CERTS']: return decoded_keys # TODO cache the keys for some amount of time (in the db, perhaps) # Download a key directly from Keycloak response = requests.get(settings.KEYCLOAK['CERTS_URL'], timeout=5) if not response: raise RuntimeError('keys not available from {}'.format( settings.KEYCLOAK['CERTS_URL'])) keys = response.json() decoded_keys = {} for key in keys['keys']: print('key: {}'.format(key)) if key['alg'] in ['RS256', 'RS384', 'RS512']: decoded_keys[key['kid']] = RSAAlgorithm.from_jwk( json.dumps(key)).public_bytes( format=serialization.PublicFormat.SubjectPublicKeyInfo, encoding=serialization.Encoding.PEM).decode('utf-8') return decoded_keys
def _get_rsa_key(token): """ Get rsa key(public key) from token Details https://jwt.io/ :param str token: Id/Access token :return: token public key after validation with RSAAlgorithm """ try: unverified_header = jwt.get_unverified_header(token) except jwt.DecodeError as exc: raise AuthError( { 'code': 'invalid_header', 'message': f'Invalid header. {str(exc)}' }, 401) if unverified_header['alg'] == 'HS256': raise AuthError( { 'code': 'invalid_header', 'message': 'Invalid header. Use an RS256 signed JWT Access Token' }, 401) jwk_keys = _json_web_keys() jwk_data = jwk_keys.get(unverified_header['kid']) if jwk_data: return RSAAlgorithm.from_jwk(jwk_data) return None
def verify_and_decode_id_token(self, id_token: str) -> Optional[dict]: """ Verifies that the received ID Token was signed and sent by the Authorization Server and that the client is one of the audiences of the key. If ID Token is valid returns a dict containing the tokens decoded information. """ unverified_header = jwt.get_unverified_header(id_token) # Get public key thumbprint kid = unverified_header.get("kid") jwks = self._retrieve_jwks_related_to_kid(kid) if jwks: alg = unverified_header.get("alg") public_key = RSAAlgorithm.from_jwk(json.dumps(jwks)) try: decoded_token = jwt.decode(id_token, public_key, audience=[self.client_id], algorithms=alg) if self.cache_nonces: # Verify that the cached nonce is present and that # the provider the nonce was initiated for, is the same # provider returning it server = cache.get(decoded_token.get("nonce")) if self.auth_server != server: return None return decoded_token except Exception as e: raise e return None
def get_keycloak_public_key_and_algorithm(token_kid): """ Get Keycloak public key and token signing algorithm :param token_kid: Token kid :return: Algorithm and public_key pair """ handle = f'{keycloak_config.get("oidc_server_url")}/protocol/openid-connect/certs' log.info(f'Getting public key for the kid={token_kid} from the keycloak...') r = requests.get(handle, verify=http_config.get('verify_cert')) if not r.ok: error = "Could not get certificates from Keycloak. " \ "Reason: [{}]: {}".format(r.status_code, r.text) logger.error(error) raise ValueError(error) try: json_response = r.json() except Exception: error = "Could not retrieve the public key. " \ "Got unexpected response: '{}'".format(r.text) logging.error(error) raise ValueError(error) try: matching_key = next((item for item in json_response.get('keys') if item['kid'] == token_kid), None) if matching_key is None: error = "No public key found for kid {}".format(token_kid) logger.error(error) raise ValueError(error) matching_key_json = json.dumps(matching_key) public_key = RSAAlgorithm.from_jwk(matching_key_json) except Exception as e: error = f'Invalid public key!. Reason: {e}' logger.error(error) raise ValueError(error) logger.info(f'The public key for the kid={token_kid} has been fetched.') return matching_key.get('alg'), public_key
def _find(self, key_id: str): if not self.keys: return None key = next(x for x in self.keys if x["kid"] == key_id) public_key = RSAAlgorithm.from_jwk(json.dumps(key)) endorsements = key.get("endorsements", []) return _OpenIdConfig(public_key, endorsements)
def _oauth_validation(cls): issuer = config.get('connect_token_issuer') cert_url = config.get('connect_token_cert') if request.httprequest.method == 'GET': mode = 'read' if request.httprequest.method == 'POST': mode = 'write' # Get public certificate of issuer for token validation try: token_data = request.httprequest.headers.get('Authorization') access_token = token_data.split()[1] _logger.info("Received access token: %s", access_token) cert = requests.get(cert_url) key_json = simplejson.dumps(cert.json()['keys'][0]) except ValueError: # If any error occurs during token and certificate retrieval, # we put a wrong certificate, and jwt library will fail # to decrypt the token, leading to unauthorized error. key_json = {} public_key = RSAAlgorithm.from_jwk(key_json) jwt_decoded = jwt.decode(access_token, key=public_key, algorithms=['RS256'], audience=issuer + '/resources', issuer=issuer) # validation # is scope read or write in scopes ? scope = jwt_decoded.get('scope') if scope and mode not in scope: raise Unauthorized() client_id = jwt_decoded.get('client_id') or jwt_decoded.get('ClientID') _logger.info("TOKEN CLIENT IS -----------------> " + client_id) return client_id
def test_authenticate_jwt(): jwt_token = """eyJhbGciOiJSUzI1NiIsImtpZCI6IjJlM2VlYjc2ODM2YWViNjBjMzA2MzNkYjM3ZGU4ZDgwOWE4MTE0YzciLCJ0eXAiOiJKV1QifQ.eyJiYW5zIjpbXSwiY2xpZW50X2lkIjoiZTdkZTkyMjZkZjRkNDAxYmJhNDRkMjQzZTkzNzgyMDQiLCJjb3VudHJ5IjoiR2xvYmFsIiwiZGlzcGxheV9uYW1lIjoiU3VwZXJ1c2VyIiwiZXhwIjoxNTgyODAwNTUxLCJpYXQiOjE1ODI3ODYxNTEsImpmbGdzIjoxLCJuYW1lc3BhY2UiOiJ2ZXJzdXNldmlsIiwicGVybWlzc2lvbnMiOltdLCJyb2xlcyI6WyJkMzJiMzM1NGRjOTg0ZjQ2YmJlMjk5ZDcwZDJlY2M3NCJdLCJzY29wZSI6ImFjY291bnQgY29tbWVyY2Ugc29jaWFsIHB1Ymxpc2hpbmcgYW5hbHl0aWNzIiwic3ViIjoiYjkzY2JlYWNhMGQwNGJiZGIxZWIxNjBjZWY4OGE0ZWIifQ.mAR7QCsp2VqSconomgNwse_aYek3T1XKmaRKGVGU0VRQYp_-wDodrA35yypswwbtjX9qhRfsK28E9lhMf9C8UAf5lLG8p0lpejFpuPJAlKe0h6g_n3iylBWZronpa0_KxF0KCHh3DZERXMUXA7Jm8GWAIerrBX6tAj1PXw4EYQ_Cm9x-Issj8hffNPcvPrH61pdvQItIgx_lA8wDgyaGZvpFmHSzjKtWkZHpvBVRh5NZzJE-8FrLYdgwipJ6wKLF6kVA8rhqSMxqC9YfuwQ58GYSyS4jFw_LQyOe_d-RKDJS-0-pphsfd8rvqGROhhmImZEo9Pr1unOYT4MgEdW28w""" jwks = """{ "keys": [ { "kty": "RSA", "use": "sig", "kid": "2e3eeb76836aeb60c30633db37de8d809a8114c7", "n": "uEvSt8ecPmI8-8_z9K5F1IzSeBze9OvR-y9U1AqUX6vncMZjJWQti05VbXUk8-UsJUI-5OkBxJ8XYy_8PIUArsTC-naoer7_XM7gvdWH_y20Vbwibbpy7ONhgACZOaeA0iUXyuKu7f5L78gyY7AedY7JJ5shvgMBeR8HJKbVSBq1H4fJqGIjPss6k5C62shiKrMbpm4q1Tg8o8tmCWm7CyyUbiUggzJusKAqc7ZccsSLRkTF5G3fN4nzBP4rwthYf2aSOVNC4Lcnqx7QsEQvpauVdR0rRuiB8ERyikWjGSVMXoZ-1YxaAUkKHtz1J4jMtNvOJa6Qw_UMbiAVhrz0Gw", "e": "AQAB" } ] }""" jwks = json.loads(jwks) jwk_key = json.dumps(jwks['keys'][0]) public_key = RSAAlgorithm.from_jwk(jwk_key) permission_rule_json = """{ "accelbytetesting": ["rolesidaccelbytetesting", "adminrolesidaccelbytetesting", "accelbyteadminrole"], "samplegame": ["rolesidsamplegame", "accelbyteadminrole"], "public": ["*"], "accelbyte": ["accelbyteadminrole"], "versusevil": ["d32b3354dc984f46bbe299d70d2ecc74"] }""" allowed, decoded = jwt.authenticate_jwt(public_key, jwt_token, permission_rule_json, verify_exp=False) result = {'bans': [], 'client_id': 'e7de9226df4d401bba44d243e9378204', 'country': 'Global', 'display_name': 'Superuser', 'exp': 1582800551, 'iat': 1582786151, 'jflgs': 1, 'namespace': 'versusevil', 'permissions': [], 'roles': ['d32b3354dc984f46bbe299d70d2ecc74'], 'scope': 'account commerce social publishing analytics', 'sub': 'b93cbeaca0d04bbdb1eb160cef88a4eb'} print(allowed, decoded) assert result == decoded
def _get_public_key(self, token): try: headers = jwt.get_unverified_header(token) except jwt.DecodeError as exc: raise TokenError(str(exc)) jwk_data = self._json_web_keys().get(headers["kid"]) return RSAAlgorithm.from_jwk(jwk_data)
async def authenticate(self, request): # pylint: disable=arguments-renamed """Authenticate the token.""" if 'Authorization' not in request.headers: return None auth = request.headers['Authorization'] token = self.get_token_from_header(authorization=auth, prefix=self.prefix) if self.jwt_oidc_test_mode: # in test mode, use a publick key to decode token directly. try: payload = jwt.decode(str.encode(token), self.jwt_oidc_test_public_key_pem, algorithms=self.algorithm) except jwt.InvalidTokenError as e: raise AuthenticationError(str(e)) else: # in production mod, get the public key from jwks_url try: unverified_header = jwt.get_unverified_header(token) except jwt.PyJWTError: raise AuthenticationError( 'Invalid header: Use an RS256 signed JWT Access Token') if unverified_header['alg'] == 'HS256': raise AuthenticationError( 'Invalid header: Use an RS256 signed JWT Access Token') if 'kid' not in unverified_header: raise AuthenticationError( 'Invalid header: No KID in token header') rsa_key = self.get_rsa_key(self.get_jwks(), unverified_header['kid']) if not rsa_key and self.caching_enabled: # Could be key rotation, invalidate the cache and try again self.cache.delete('jwks') rsa_key = self.get_rsa_key(self.get_jwks(), unverified_header['kid']) if not rsa_key: raise AuthenticationError( 'invalid_header: Unable to find jwks key referenced in token' ) public_key = RSAAlgorithm.from_jwk(json.dumps(rsa_key)) try: payload = jwt.decode(token, public_key, algorithms=self.algorithm, audience=self.audience) except jwt.InvalidTokenError as e: raise AuthenticationError(str(e)) return AuthCredentials(['authenticated' ]), JWTUser(username=payload['username'], token=token, payload=payload)
def jwt_key_to_pem(self, key_json_dict): """ Builds a PEM formatted key string from a JWT public key dict. """ pub_key = RSAAlgorithm.from_jwk(json.dumps(key_json_dict)) return pub_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)
def test_rsa_public_key_to_jwk_works_with_from_jwk(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path("testkey_rsa.pub")) as rsa_key: orig_key = algo.prepare_key(rsa_key.read()) parsed_key = algo.from_jwk(algo.to_jwk(orig_key)) assert parsed_key.public_numbers() == orig_key.public_numbers()
def get_oidc_configuration(app): OIDC_ISSUER_URL_BY_PROVIDER = { 'azure': f"https://login.microsoftonline.com/{app.config['AZURE_TENANT']}/v2.0", 'cognito': f"https://cognito-idp.{app.config['AWS_REGION']}.amazonaws.com/{app.config['COGNITO_USER_POOL_ID']}", 'gitlab': app.config['GITLAB_URL'], 'google': 'https://accounts.google.com', 'keycloak': f"{app.config['KEYCLOAK_URL']}/auth/realms/{app.config['KEYCLOAK_REALM']}" } issuer_url = OIDC_ISSUER_URL_BY_PROVIDER.get( app.config['AUTH_PROVIDER']) or app.config['OIDC_ISSUER_URL'] if not issuer_url: raise ApiError( 'Must define Issuer URL (OIDC_ISSUER_URL) in server configuration to use OpenID Connect.', 503) discovery_doc_url = issuer_url.strip( '/') + '/.well-known/openid-configuration' try: r = requests.get(discovery_doc_url, timeout=2) config = r.json() except Exception as e: raise ApiError( f'Could not get OpenID configuration from well known URL: {str(e)}', 503) if 'issuer' not in config: error = config.get('error') or config.get('message') or config raise ApiError(f'OpenID Connect issuer response invalid: {error}') if config['issuer'].format( tenantid=app.config['AZURE_TENANT']) != issuer_url: raise ApiError( 'Issuer Claim does not match Issuer URL used to retrieve OpenID configuration', 503) if app.config['OIDC_VERIFY_TOKEN']: try: jwks_uri = config['jwks_uri'] r = requests.get(jwks_uri, timeout=2) keys = { k['kid']: RSAAlgorithm.from_jwk(json.dumps(k)) for k in r.json()['keys'] } except Exception as e: raise ApiError( f'Could not get OpenID JWT Key Set from JWKS URL: {str(e)}', 503) else: keys = {} return config, keys
def _create_jwt(payload, key_id=None): if key_id is None: key_id = JWK_PRIVATE_KEY["kid"] key = json.dumps(JWK_PRIVATE_KEY) secret = RSAAlgorithm.from_jwk(key) return jwt.encode( payload, secret, algorithm="RS256", headers={"kid": key_id, "alg": "RS256"} )
def test_rsa_private_key_to_jwk_works_with_from_jwk(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'r') as rsa_key: orig_key = algo.prepare_key(force_unicode(rsa_key.read())) parsed_key = algo.from_jwk(algo.to_jwk(orig_key)) assert parsed_key.private_numbers() == orig_key.private_numbers() assert parsed_key.private_numbers().public_numbers == orig_key.private_numbers().public_numbers
def _jwt_key_to_pem(self, key_json_dict): """ Builds a PEM formatted key string from a JWT public key dict. """ pub_key = RSAAlgorithm.from_jwk(json.dumps(key_json_dict)) return pub_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo )
def get_latest_public_key(sso_url, realm): url = "%s/auth/realms/%s/.well-known/openid-configuration" % (sso_url, realm) jwks_uri = requests.get(url).json()["jwks_uri"] jwks = requests.get(jwks_uri).json()["keys"] return RSAAlgorithm.from_jwk(json.dumps(jwks[0])).public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, )
def get_jwk(self, header: dict): """Get JWK matching the kid in the token.""" # Cache the key set once retrieved. if not self._jwks: self._jwks = self._get_jwks() key = self._find_jwk(header) if key: return RSAAlgorithm.from_jwk(json.dumps(key))
def create_jwt_token(private_key, payload): key = json.dumps(private_key) key_id = private_key['kid'] secret = RSAAlgorithm.from_jwk(key) return jwt.encode(payload, secret, algorithm='RS256', headers={'kid': key_id})
def normalize_processor_response(self, response): decision_map = { 'AUTHORIZED': Decision.accept, 'PARTIAL_AUTHORIZED': Decision.decline, 'AUTHORIZED_PENDING_REVIEW': Decision.review, 'AUTHORIZED_RISK_DECLINED': Decision.decline, 'PENDING_AUTHENTICATION': Decision.decline, 'PENDING_REVIEW': Decision.review, 'DECLINED': Decision.decline, 'INVALID_REQUEST': Decision.error, } response_json = self.serialize_order_completion(response) if isinstance(response, ApiException): decision = decision_map.get(response_json.get('status'), response_json.get('status')) return UnhandledCybersourceResponse( decision=decision, duplicate_payment=(decision == Decision.error and response_json.get('reason') == 'DUPLICATE_REQUEST'), partial_authorization=False, currency=None, total=None, card_number=None, card_type=None, transaction_id=None, order_id=None, raw_json=response_json, ) decoded_capture_context = jwt.decode(self.capture_context['key_id'], verify=False) jwk = RSAAlgorithm.from_jwk( json.dumps(decoded_capture_context['flx']['jwk'])) decoded_payment_token = jwt.decode(self.transient_token_jwt, key=jwk, algorithms=['RS256']) decision = decision_map.get(response.status, response.status) return UnhandledCybersourceResponse( decision=decision, duplicate_payment=(decision == Decision.error and response.reason == 'DUPLICATE_REQUEST'), partial_authorization=response.status == 'PARTIAL_AUTHORIZED', currency=response.order_information.amount_details.currency, total=Decimal( response.order_information.amount_details.total_amount), card_number=decoded_payment_token['data']['number'], card_type=CYBERSOURCE_CARD_TYPE_MAP.get( response.payment_information.tokenized_card.type), transaction_id=response.processor_information.transaction_id, order_id=response.client_reference_information.code, raw_json=response_json, )
def validate_jwt_token(jwt_token): if jwt_token == None: raise Exception("Invalid authentication header") token_array = jwt_token.split(" ") if len(token_array) != 2: raise Exception("Invalid authentication header") token = token_array[1] try: decoded_unverified = jwt.decode(token, verify=False) except: raise Exception("Error decoding token") iss = decoded_unverified["iss"] aud = decoded_unverified["aud"] if aud == None: raise Exception("Invalid token aud") if iss == None: raise Exception("Invalid token iss") targetUrl = iss + ".well-known/jwks.json" try: jsonurl = urllib.request.urlopen(targetUrl) jwks = json.loads(jsonurl.read()) except: raise Exception("Error requesting keys from token issuer") try: unverified_header = jwt.get_unverified_header(token) if unverified_header["alg"] == "HS256": raise Exception("Invalid keys alg") rsa_key = {} for key in jwks["keys"]: if key["kid"] == unverified_header["kid"]: rsa_key = { "kty": key["kty"], "kid": key["kid"], "use": key["use"], "n": key["n"], "e": key["e"] } public_key = RSAAlgorithm.from_jwk(json.dumps(rsa_key)) decoded = jwt.decode(token, public_key, algorithms='RS256', audience=aud) if aud == decoded["aud"]: return decoded except: raise Exception("Error validating token") raise Exception("Unhandled error processing token")
def load_rsa_pub_key(): with open(os.path.join(BASE_PATH, 'jwk_rsa_pub.json'), 'r') as infile: return RSAAlgorithm.from_jwk(infile.read())