def fetch_jwk_for(id_token=None): if id_token is None: raise NameError('id_token is required') jwks_uri = "{}/v1/keys".format(config['oidc']['issuer']) unverified_header = jws.get_unverified_header(id_token) key_id = None if 'kid' in unverified_header: key_id = unverified_header['kid'] else: raise ValueError('The id_token header must contain a "kid"') if key_id in public_key_cache: return public_key_cache[key_id] r = requests.get(jwks_uri) jwks = r.json() for key in jwks['keys']: jwk_id = key['kid'] public_key_cache[jwk_id] = key if key_id in public_key_cache: return public_key_cache[key_id] else: raise RuntimeError("Unable to fetch public key from jwks_uri")
def fetch_jwt_public_key_for(id_token=None): if id_token is None: raise NameError('id_token is required') dirty_header = jws.get_unverified_header(id_token) cleaned_key_id = None if 'kid' in dirty_header: dirty_key_id = dirty_header['kid'] cleaned_key_id = re.sub(not_alpha_numeric, '', dirty_key_id) else: raise ValueError('The id_token header must contain a "kid"') if cleaned_key_id in public_keys: return public_keys[cleaned_key_id] unverified_claims = jwt.get_unverified_claims(id_token) dirty_url = urlparse.urlparse(unverified_claims['iss']) if domain_name_for(dirty_url) not in allowed_domains: raise ValueError('The domain in the issuer claim is not allowed') cleaned_issuer = dirty_url.geturl() oidc_discovery_url = "{}/.well-known/openid-configuration".format( cleaned_issuer) r = requests.get(oidc_discovery_url) openid_configuration = r.json() jwks_uri = openid_configuration['jwks_uri'] r = requests.get(jwks_uri) jwks = r.json() for key in jwks['keys']: jwk_id = key['kid'] public_keys[jwk_id] = key if cleaned_key_id in public_keys: return public_keys[cleaned_key_id] else: raise RuntimeError("Unable to fetch public key from jwks_uri")
def checkJWKS(token): jwks = False tokendecode = None tokendecodeheader = None pubcert = None message = None #not a JWT at all -- not an error condition, just a reality. try: tokendecode = jjws.get_unverified_claims(token) tokendecodeheader = jjws.get_unverified_header(token) jwks = True except Exception as e: print(e) return False for uri in JWKSUri.objects.all(): jwks_json = _getjwks(uri.URL) if not jwks_json: break for key in jwks_json.get('keys', None): try: jwks = _decode_with_cert(token, key, hint=uri.hint) if jwks: return jwks except Exception as e: print("check JWKS %s" % e) continue continue return False
def fetch_jwk_for(issuer, id_token=None): if id_token is None: raise NameError('id_token is required') # FIXME: This should be pulled from the OpenID connect Discovery Document jwks_uri = '{}/v1/keys'.format(issuer) unverified_header = jws.get_unverified_header(id_token) key_id = None if 'kid' in unverified_header: key_id = unverified_header['kid'] else: raise ValueError('The id_token header must contain a "kid"') if key_id in settings.PUBLIC_KEY_CACHE: return settings.PUBLIC_KEY_CACHE[key_id] r = requests.get(jwks_uri) jwks = r.json() for key in jwks['keys']: jwk_id = key['kid'] settings.PUBLIC_KEY_CACHE[jwk_id] = key if key_id in settings.PUBLIC_KEY_CACHE: return settings.PUBLIC_KEY_CACHE[key_id] else: raise RuntimeError('Unable to fetch public key from jwks_uri')
def _try_decode(self, openid_credentials: dict) -> dict: id_token = openid_credentials["id_token"] # "access_token" must only be provided if claim is provided, else "jwt.decode" throws... if b"at_hash" in jws.get_unverified_claims(id_token): access_token = openid_credentials["access_token"] else: access_token = None header = jws.get_unverified_header(id_token) verified_id_token = jwt.decode( id_token, self._jwk_pkey.key(header["kid"]), issuer=self._issuer, access_token=access_token, audience=self._client_id, ) return verified_id_token
def __checkJwsExpiration(self, payload): ''' Check if JWS signature has not expired. ''' header = jws.get_unverified_header(payload) if 'exp' not in header: raise HyperwalletException('While trying to verify JWS signature no [exp] header is found') exp = header['exp'] if not isinstance(exp, (int, long)): raise HyperwalletException('Wrong value in [exp] header of JWS signature, must be integer') if exp < int(time.time()): raise HyperwalletException('JWS signature has expired, checked by [exp] JWS header')
def _decode_with_cert(token, key, hint=None): kid = key.get('kid', None) tokenkid = None pubcert = None jwtson = None tokendecode = jjws.get_unverified_claims(token) tokenkid = jjws.get_unverified_header(token).get("kid") if (tokenkid and kid) and (kid == tokenkid): try: jwtson = jjwt.decode(token, key, algorithms='RS256', options={'verify_aud': False}) return jwtson except Exception as e: print("kid in token: Cannot be introspected in JWKS %s" % e)
def jwt_launch_via_json(): from jose import jwk, jws, jwt from jose.utils import base64url_decode import requests #from keys import public_key, private_key print("=-=-=-=-=-=- REQUEST FORM -=-=-=-=-=") print(json.dumps(request.form, indent=2)) #jwt id_token = request.form.get('id_token') audience = '407321823' #key id kid = jws.get_unverified_header(id_token)['kid'] #get platform web keys from lti-ri r = requests.get(settings.JSON_KEY_URL) #todo - get key based on KID web_keys = r.json()['keys'][0] #create key from web keys key = jwk.construct(web_keys) try: #Verify JWT lti_data = jwt.decode(id_token, web_keys, algorithms=['RS256'], audience=audience) print("=-=-=-=-=-=- LTI DATA -=-=-=-=-=") print(lti_data) return "Hello %s! <br> JWT Successfully Decoded and verified." % lti_data[ 'name'] except: return "There was an error verifying the JWT." return "Should not get here"
def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None): """Verifies a JWT string's signature and validates reserved claims. Args: token (str): A signed JWS to be verified. key (str or dict): A key to attempt to verify the payload with. Can be individual JWK or JWK set. algorithms (str or list): Valid algorithms that should be used to verify the JWS. audience (str): The intended audience of the token. If the "aud" claim is included in the claim set, then the audience must be included and must equal the provided claim. issuer (str or iterable): Acceptable value(s) for the issuer of the token. If the "iss" claim is included in the claim set, then the issuer must be given and the claim in the token must be among the acceptable values. subject (str): The subject of the token. If the "sub" claim is included in the claim set, then the subject must be included and must equal the provided claim. access_token (str): An access token string. If the "at_hash" claim is included in the claim set, then the access_token must be included, and it must match the "at_hash" claim. options (dict): A dictionary of options for skipping validation steps. defaults = { 'verify_signature': True, 'verify_aud': True, 'verify_iat': True, 'verify_exp': True, 'verify_nbf': True, 'verify_iss': True, 'verify_sub': True, 'verify_jti': True, 'verify_at_hash': True, 'require_aud': False, 'require_iat': False, 'require_exp': False, 'require_nbf': False, 'require_iss': False, 'require_sub': False, 'require_jti': False, 'require_at_hash': False, 'leeway': 0, } Returns: dict: The dict representation of the claims set, assuming the signature is valid and all requested data validation passes. Raises: JWTError: If the signature is invalid in any way. ExpiredSignatureError: If the signature has expired. JWTClaimsError: If any claim is invalid in any way. Examples: >>> payload = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' >>> jwt.decode(payload, 'secret', algorithms='HS256') """ defaults = { 'verify_signature': True, 'verify_aud': True, 'verify_iat': True, 'verify_exp': True, 'verify_nbf': True, 'verify_iss': True, 'verify_sub': True, 'verify_jti': True, 'verify_at_hash': True, 'require_aud': False, 'require_iat': False, 'require_exp': False, 'require_nbf': False, 'require_iss': False, 'require_sub': False, 'require_jti': False, 'require_at_hash': False, 'leeway': 0, } if options: defaults.update(options) verify_signature = defaults.get('verify_signature', True) try: payload = jws.verify(token, key, algorithms, verify=verify_signature) except JWSError as e: raise JWTError(e) # Needed for at_hash verification algorithm = jws.get_unverified_header(token)['alg'] try: claims = json.loads(payload.decode('utf-8')) except ValueError as e: raise JWTError('Invalid payload string: %s' % e) if not isinstance(claims, Mapping): raise JWTError('Invalid payload string: must be a json object') _validate_claims(claims, audience=audience, issuer=issuer, subject=subject, algorithm=algorithm, access_token=access_token, options=defaults) return claims
def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None): """Verifies a JWT string's signature and validates reserved claims. Args: token (str): A signed JWS to be verified. key (str): A key to attempt to verify the payload with. algorithms (str or list): Valid algorithms that should be used to verify the JWS. audience (str): The intended audience of the token. If the "aud" claim is included in the claim set, then the audience must be included and must equal the provided claim. issuer (str or iterable): Acceptable value(s) for the issuer of the token. If the "iss" claim is included in the claim set, then the issuer must be given and the claim in the token must be among the acceptable values. subject (str): The subject of the token. If the "sub" claim is included in the claim set, then the subject must be included and must equal the provided claim. access_token (str): An access token returned alongside the id_token during the authorization grant flow. If the "at_hash" claim is included in the claim set, then the access_token must be included, and it must match the "at_hash" claim. options (dict): A dictionary of options for skipping validation steps. defaults = { 'verify_signature': True, 'verify_aud': True, 'verify_iat': True, 'verify_exp': True, 'verify_nbf': True, 'verify_iss': True, 'verify_sub': True, 'verify_jti': True, 'leeway': 0, } Returns: dict: The dict representation of the claims set, assuming the signature is valid and all requested data validation passes. Raises: JWTError: If the signature is invalid in any way. Examples: >>> payload = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' >>> jwt.decode(payload, 'secret', algorithms='HS256') """ defaults = { 'verify_signature': True, 'verify_aud': True, 'verify_iat': True, 'verify_exp': True, 'verify_nbf': True, 'verify_iss': True, 'verify_sub': True, 'verify_jti': True, 'verify_at_hash': True, 'leeway': 0, } if options: defaults.update(options) verify_signature = defaults.get('verify_signature', True) try: payload = jws.verify(token, key, algorithms, verify=verify_signature) except JWSError as e: raise JWTError(e) # Needed for at_hash verification algorithm = jws.get_unverified_header(token)['alg'] try: claims = json.loads(payload.decode('utf-8')) except ValueError as e: raise JWTError('Invalid payload string: %s' % e) if not isinstance(claims, Mapping): raise JWTError('Invalid payload string: must be a json object') _validate_claims(claims, audience=audience, issuer=issuer, subject=subject, algorithm=algorithm, access_token=access_token, options=defaults) return claims
def get_token_algorithm(token): unverified_header = jws.get_unverified_header(token) return unverified_header.get('alg')