def get_credentials(): expected_errors = { KeyError: WRONG_PAYLOAD_STRUCTURE, AssertionError: JWKS_HOST_MISSING, InvalidSignatureError: WRONG_KEY, DecodeError: WRONG_JWT_STRUCTURE, MissingRequiredClaimError: WRONG_PAYLOAD_STRUCTURE, InvalidAudienceError: WRONG_AUDIENCE, PyJWKClientError: KID_NOT_FOUND, URLError: WRONG_JWKS_HOST, HTTPError: WRONG_JWKS_HOST } try: token = get_auth_token() jwks_host = jwt.decode(token, options={ 'verify_signature': False }).get('jwks_host') assert jwks_host jwks_client = PyJWKClient(f'https://{jwks_host}/.well-known/jwks') signing_key = jwks_client.get_signing_key_from_jwt(token) aud = request.url_root payload = jwt.decode(token, signing_key.key, algorithms=['RS256'], audience=[aud.rstrip('/')]) current_app.config['SERVER_IP'] = payload['SERVER_IP'] set_ctr_entities_limit(payload) return payload['user'], payload['pass'] except tuple(expected_errors) as error: raise AuthorizationError(expected_errors[error.__class__])
def __init__( self, algorithm, signing_key=None, verifying_key="", audience=None, issuer=None, jwk_url: str = None, leeway: Union[float, int, timedelta] = None, json_encoder: Optional[Type[json.JSONEncoder]] = None, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.verifying_key = verifying_key self.audience = audience self.issuer = issuer if JWK_CLIENT_AVAILABLE: self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None else: self.jwks_client = None self.leeway = leeway self.json_encoder = json_encoder
def validated_token(token, verify=True): """Validate a token and return decoded token.""" url = ( requests.get( config.oauth.google_openid_config_uri(default=DEFAULT_GOOGLE_OPENID_CFG_URI) ) .json() .get( config.oauth.google_openid_jkws_key( default=DEFAULT_GOOGLE_OPENID_CFG_JWKS_KEY ) ) ) logger.info("JWK url is %s", url) jwks_client = PyJWKClient(url) signing_key = jwks_client.get_signing_key_from_jwt(token) data = jwt.decode( token, signing_key.key, algorithms=["RS256"], audience=config.oauth.audience(default=DEFAULT_AUDIENCE, cast=split_list), options={"verify_signature": verify}, ) return data
def verify_id_token(id_token: str, bundle_id=None) -> dict: if bundle_id is None: bundle_id = APPLE_SIGN_IN_AUD[0] if bundle_id not in APPLE_SIGN_IN_AUD: raise falcon.HTTPForbidden(description="Not allow bundle id.") jwt_header = jwt.get_unverified_header(id_token) # get kid to select public key kid = jwt_header.get("kid", None) if kid is None: raise falcon.HTTPUnauthorized( description= "Not found kid from your id_token, check your token is by apple sign in." ) jwks_client = PyJWKClient(APPLE_AUTH_KEYS_URL) signing_key = jwks_client.get_signing_key_from_jwt(id_token) jwt_decode = jwt.decode( id_token, signing_key.key, algorithms=["RS256"], audience=bundle_id, options={"verify_exp": False}, ) return jwt_decode
def test_get_jwk_set(self, mocked_response): url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json" with requests_mock.mock() as m: m.get(url, json=mocked_response) jwks_client = PyJWKClient(url) jwk_set = jwks_client.get_jwk_set() assert len(jwk_set.keys) == 1
def test_get_signing_keys(self, mocked_response): url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json" with requests_mock.mock() as m: m.get(url, json=mocked_response) jwks_client = PyJWKClient(url) signing_keys = jwks_client.get_signing_keys() assert len(signing_keys) == 1 assert isinstance(signing_keys[0], PyJWK)
async def test_full_flow(self) -> None: key = await random_key() uuid_identifier = uuid4() # Register a new key # This is done using direct json since KeyRequest.dump doesn't dump key (on purpose) request = RegisterKeyRequest(key, uuid4()) response = await self.client.post( "/register_key", json=self.register_key_request_schema.dump(request)) assert response.status == HTTPStatus.CREATED # Check that the key was registered request2 = KeyRequest(key) response = await self.client.post( "/is_key_registered", json=self.key_request_schema.dump(request2)) assert response.status == HTTPStatus.OK # Authenticate and get access + refres tokens request = self.auth_request_schema.dump( AuthRequest(key=key, identifier=uuid_identifier)) response = await self.client.post("/authenticate", json=request) assert response.status == HTTPStatus.OK response_json = await response.json() # Check access token is valid request = self.jwt_validate_request_schema.dump( JWTValidateRequest(jwt=response_json["jwt"])) response = await self.client.post("/validate", json=request) assert response.status == HTTPStatus.OK # Use refresh token to create a new access token request = self.refresh_request_schema.dump( RefreshRequest(refresh_token=response_json["refresh_token"], identifier=uuid_identifier)) response = await self.client.post("/refresh", json=request) assert response.status == HTTPStatus.OK response_json = await response.json() # Check that new access token is also valid token = response_json["jwt"] request = self.jwt_validate_request_schema.dump( JWTValidateRequest(jwt=token)) response = await self.client.post("/validate", json=request) assert response.status == HTTPStatus.OK # Test JWKS response = await self.client.get("/jwks") assert response.status == HTTPStatus.OK response_json = await response.json() jwks_client = PyJWKClient("") jwks_client.fetch_data = MagicMock( return_value=response_json) # type: ignore assert jwks_client.get_signing_key_from_jwt(token)
def _get_public_key(self): if self.header["alg"][:2] == "RS": try: wk_res = requests.get(self.payload["iss"] + "/.well-known/openid-configuration", verify=False) jwks_uri = wk_res.json()["jwks_uri"] jwks_client = PyJWKClient(jwks_uri) return jwks_client.get_signing_key_from_jwt( self.encoded).key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) except Exception as ex: self.errors.append("Error getting public Key: {0}".format( str(ex))) return None
async def query_jwks(): #dump('NO CACHE, TRY CONSUMER: ' + consumer_name) # Get default or overridden jwks_url jwks_url = consumer.jwks_url or default_jwks_url self.log.debug( 'Verifying JWT via JWKS URL {}'.format( jwks_url)) # Get signing_key from jwks jwks_client = PyJWKClient(jwks_url) signing_key = jwks_client.get_signing_key_from_jwt( token) return Dict( decode(jwt=token, key=signing_key.key, audience=consumer.aud, algorithms=consumer.algorithms))
def validate_id_token(identity_token): identity_token = base64.b64decode(identity_token) jwk_client = PyJWKClient('https://appleid.apple.com/auth/keys') signing_key = jwk_client.get_signing_key_from_jwt(identity_token) try: data = jwt.decode(identity_token, signing_key.key, algorithms=['RS256'], audience=os.environ['BUNDLE_ID'], issuer='https://appleid.apple.com', options={ 'verify_aud': True, 'verify_exp': True, 'verify_iss': True }) except exceptions.InvalidTokenError as error: return False return True
def __init__( self, algorithm, signing_key=None, verifying_key="", audience=None, issuer=None, jwk_url: str = None, leeway=0, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.verifying_key = verifying_key self.audience = audience self.issuer = issuer if JWK_CLIENT_AVAILABLE: self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None else: self.jwks_client = None self.leeway = leeway
def __init__( self, algorithm, signing_key=None, verifying_key=None, audience=None, issuer=None, jwk_url: str = None, leeway=0, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.audience = audience self.issuer = issuer self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None self.leeway = leeway if algorithm.startswith("HS"): self.verifying_key = signing_key else: self.verifying_key = verifying_key
def test_get_signing_key_caches_result(self): url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json" kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw" jwks_client = PyJWKClient(url) with mocked_response(RESPONSE_DATA): jwks_client.get_signing_key(kid) # mocked_response does not allow urllib.request.urlopen to be called twice # so a second mock is needed with mocked_response(RESPONSE_DATA) as repeated_call: jwks_client.get_signing_key(kid) assert repeated_call.call_count == 0
class TokenBackend: def __init__( self, algorithm, signing_key=None, verifying_key="", audience=None, issuer=None, jwk_url: str = None, leeway: Union[float, int, timedelta] = None, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.verifying_key = verifying_key self.audience = audience self.issuer = issuer if JWK_CLIENT_AVAILABLE: self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None else: self.jwks_client = None self.leeway = leeway def _validate_algorithm(self, algorithm): """ Ensure that the nominated algorithm is recognized, and that cryptography is installed for those algorithms that require it """ if algorithm not in ALLOWED_ALGORITHMS: raise TokenBackendError( format_lazy(_("Unrecognized algorithm type '{}'"), algorithm)) if algorithm in algorithms.requires_cryptography and not algorithms.has_crypto: raise TokenBackendError( format_lazy( _("You must have cryptography installed to use {}."), algorithm)) def get_leeway(self) -> timedelta: if self.leeway is None: return timedelta(seconds=0) elif isinstance(self.leeway, (int, float)): return timedelta(seconds=self.leeway) elif isinstance(self.leeway, timedelta): return self.leeway else: raise TokenBackendError( format_lazy( _("Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." ), type(self.leeway), )) def get_verifying_key(self, token): if self.algorithm.startswith("HS"): return self.signing_key if self.jwks_client: return self.jwks_client.get_signing_key_from_jwt(token).key return self.verifying_key def encode(self, payload): """ Returns an encoded token for the given payload dictionary. """ jwt_payload = payload.copy() if self.audience is not None: jwt_payload["aud"] = self.audience if self.issuer is not None: jwt_payload["iss"] = self.issuer token = jwt.encode(jwt_payload, self.signing_key, algorithm=self.algorithm) if isinstance(token, bytes): # For PyJWT <= 1.7.1 return token.decode("utf-8") # For PyJWT >= 2.0.0a1 return token def decode(self, token, verify=True): """ Performs a validation of the given token and returns its payload dictionary. Raises a `TokenBackendError` if the token is malformed, if its signature check fails, or if its 'exp' claim indicates it has expired. """ try: return jwt.decode( token, self.get_verifying_key(token), algorithms=[self.algorithm], audience=self.audience, issuer=self.issuer, leeway=self.get_leeway(), options={ "verify_aud": self.audience is not None, "verify_signature": verify, }, ) except InvalidAlgorithmError as ex: raise TokenBackendError(_("Invalid algorithm specified")) from ex except InvalidTokenError: raise TokenBackendError(_("Token is invalid or expired"))
import os import logging import jwt from jwt import PyJWKClient try: region = os.environ["AWS_REGION"] userPoolId = os.environ["USER_POOL_ID"] url = ( f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json" ) app_client = os.environ["APP_CLIENT_ID"] # fetching jwks jwks_client = PyJWKClient(url) except Exception as e: logging.error(e) raise ("Unable to download JWKS") def return_response(isAuthorized, other_params={}): return {"isAuthorized": isAuthorized, "context": other_params} def lambda_handler(event, context): try: # fetching access token from event token = event["headers"]["authorization"]
def verify_jwt_using_jwks(self, token, jwks_url, audience): jwks_client = PyJWKClient(jwks_url) signing_key = jwks_client.get_signing_key_from_jwt(token) secret = signing_key.key return self.verify_jwt_using_secret(token, secret, audience)
import os import json import time import jwt from jwt import PyJWKClient from jwt.exceptions import DecodeError, ExpiredSignatureError, InvalidTokenError region = os.environ['AWS_REGION'] user_pool_id = os.environ['USER_POOL_ID'] app_client_id = os.environ['APP_CLIENT_ID'] cognito_keys_url = "https://cognito-idp.{}.amazonaws.com/{}/.well-known/jwks.json".format( region, user_pool_id) jwks_client = PyJWKClient(cognito_keys_url) # Expected incoming payload: # { # "authorizationToken": "ExampleAUTHtoken123123123", # "requestContext": { # "apiId": "aaaaaa123123123example123", # "accountId": "111122223333", # "requestId": "f4081827-1111-4444-5555-5cf4695f339f", # "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n", # "operationName": "MyQuery", # "variables": {} # } # } # # Response payload:
class TokenBackend: def __init__( self, algorithm, signing_key=None, verifying_key=None, audience=None, issuer=None, jwk_url: str = None, leeway=0, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.audience = audience self.issuer = issuer self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None self.leeway = leeway if algorithm.startswith("HS"): self.verifying_key = signing_key else: self.verifying_key = verifying_key def _validate_algorithm(self, algorithm): """ Ensure that the nominated algorithm is recognized, and that cryptography is installed for those algorithms that require it """ if algorithm not in ALLOWED_ALGORITHMS: raise TokenBackendError( format_lazy(_("Unrecognized algorithm type '{}'"), algorithm)) if algorithm in algorithms.requires_cryptography and not algorithms.has_crypto: raise TokenBackendError( format_lazy( _("You must have cryptography installed to use {}."), algorithm)) def get_verifying_key(self, token): if self.algorithm.startswith("HS"): return self.signing_key if self.jwks_client: return self.jwks_client.get_signing_key_from_jwt(token).key return self.verifying_key def encode(self, payload): """ Returns an encoded token for the given payload dictionary. """ jwt_payload = payload.copy() if self.audience is not None: jwt_payload['aud'] = self.audience if self.issuer is not None: jwt_payload['iss'] = self.issuer token = jwt.encode(jwt_payload, self.signing_key, algorithm=self.algorithm) if isinstance(token, bytes): # For PyJWT <= 1.7.1 return token.decode('utf-8') # For PyJWT >= 2.0.0a1 return token def decode(self, token, verify=True): """ Performs a validation of the given token and returns its payload dictionary. Raises a `TokenBackendError` if the token is malformed, if its signature check fails, or if its 'exp' claim indicates it has expired. """ try: return jwt.decode( token, self.get_verifying_key(token), algorithms=[self.algorithm], audience=self.audience, issuer=self.issuer, leeway=self.leeway, options={ 'verify_aud': self.audience is not None, 'verify_signature': verify, }, ) except InvalidAlgorithmError as ex: raise TokenBackendError(_('Invalid algorithm specified')) from ex except InvalidTokenError: raise TokenBackendError(_('Token is invalid or expired'))
def _get_key(self, kid): if not PyJWKClient: raise NotImplementedError("PyJWK isn't supported") jwks_client = PyJWKClient(self.public_key_jwk_uri, cache_keys=False) return jwks_client.get_signing_key(kid).key
def _get_key(self, kid): jwks_client = PyJWKClient(self.public_key_jwk_uri, cache_keys=False) return jwks_client.get_signing_key(kid).key
def get_jwks_client(): well_known_metadata = get_well_known_metadata() jwks_client = PyJWKClient(well_known_metadata["jwks_uri"]) return jwks_client