def validate_and_return_id_token(self, id_token, access_token): """ Validates the id_token according to the steps at http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation. """ client_id, client_secret = self.get_key_and_secret() key = self.find_valid_key(id_token) alg = key['alg'] rsakey = jwk.construct(key) try: claims = jwt.decode( id_token, rsakey.to_pem().decode('utf-8'), algorithms=[alg], audience=client_id, issuer=self.id_token_issuer(), access_token=access_token, options=self.JWT_DECODE_OPTIONS, ) except ExpiredSignatureError: raise AuthTokenError(self, 'Signature has expired') except JWTClaimsError: raise AuthTokenError(self, 'Invalid claims') except JWTError: raise AuthTokenError(self, 'Invalid signature') self.validate_claims(claims) return claims
def validate_and_return_id_token(self, id_token, access_token): """ Validates the id_token according to the steps at http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation. """ client_id, client_secret = self.get_key_and_secret() key = self.find_valid_key(id_token) if not key: raise AuthTokenError(self, 'Signature verification failed') rsakey = jwk.construct(key, algorithm=ALGORITHMS.RS256) try: claims = jwt.decode( id_token, rsakey.to_pem().decode('utf-8'), algorithms=[ALGORITHMS.HS256, ALGORITHMS.RS256, ALGORITHMS.ES256], audience=client_id, issuer=self.id_token_issuer(), access_token=access_token, options=self.JWT_DECODE_OPTIONS, ) except ExpiredSignatureError: raise AuthTokenError(self, 'Signature has expired') except JWTClaimsError as error: raise AuthTokenError(self, str(error)) except JWTError: raise AuthTokenError(self, 'Invalid signature') self.validate_claims(claims)
def request_access_token(self, *args, **kwargs): response = super(ADFSOAuth2, self).request_access_token(*args, **kwargs) try: self.token_payload = jwt.decode( response.get('access_token'), audience=self.get_scope_argument()[self.SCOPE_PARAMETER_NAME], key=self.get_token_key(), leeway=self.setting('LEEWAY', 0), iss=self.issuer_url(), options=dict( verify_signature=True, verify_exp=True, verify_nbf=False, verify_iat=self.setting('VERIFY_IAT', True), verify_aud=True, verify_iss=True, require_exp=True, require_iat=True, require_nbf=False ) ) except jwt.InvalidTokenError as exc: raise AuthTokenError(self, exc) return response
def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') # decode the JWT header as JSON dict jwt_header = json.loads( base64.b64decode(id_token.split('.', 1)[0]).decode() ) # get key id and algorithm key_id = jwt_header['kid'] algorithm = jwt_header['alg'] verify = os.environ.get('OAUTH2_VERIFY', False) key = '' try: # retrieve certificate for key_id if verify: certificate = self.get_certificate(key_id) key = certificate.public_key() return jwt_decode( id_token, verify=verify, key=key, algorithms=algorithm, audience=self.setting('KEY') ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error)
def validate_claims(self, id_token): if id_token['iss'] != self.id_token_issuer(): raise AuthTokenError(self, 'Invalid issuer') client_id, __ = self.get_key_and_secret() if isinstance(id_token['aud'], six.string_types): id_token['aud'] = [id_token['aud']] if client_id not in id_token['aud']: raise AuthTokenError(self, 'Invalid audience') if len(id_token['aud']) > 1 and 'azp' not in id_token: raise AuthTokenError(self, 'Incorrect id_token: azp') if 'azp' in id_token and id_token['azp'] != client_id: raise AuthTokenError(self, 'Incorrect id_token: azp') utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) if utc_timestamp > id_token['exp']: raise AuthTokenError(self, 'Signature has expired') if 'nbf' in id_token and utc_timestamp < id_token['nbf']: raise AuthTokenError(self, 'Incorrect id_token: nbf') # Verify the token was issued in the last 10 minutes iat_leeway = self.setting('ID_TOKEN_MAX_AGE', self.ID_TOKEN_MAX_AGE) if utc_timestamp > id_token['iat'] + iat_leeway: raise AuthTokenError(self, 'Incorrect id_token: iat')
def validate_claims(self, id_token): """Validate decoded JWT token.""" if id_token["iss"] != self.id_token_issuer(): raise AuthTokenError(self, "Invalid issuer") client_id = self.setting("KEY") if isinstance(id_token["aud"], str): id_token["aud"] = [id_token["aud"]] if client_id not in id_token["aud"]: raise AuthTokenError(self, "Invalid audience") if len(id_token["aud"]) > 1 and "azp" not in id_token: raise AuthTokenError(self, "Incorrect id_token: azp") if "azp" in id_token and id_token["azp"] != client_id: raise AuthTokenError(self, "Incorrect id_token: azp") utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) if utc_timestamp > id_token["exp"]: raise AuthTokenError(self, "Signature has expired") if "nbf" in id_token and utc_timestamp < id_token["nbf"]: raise AuthTokenError(self, "Incorrect id_token: nbf") # Verify the token was issued in the last 10 minutes iat_leeway = self.setting("ID_TOKEN_MAX_AGE", self.ID_TOKEN_MAX_AGE) if utc_timestamp > id_token["iat"] + iat_leeway: raise AuthTokenError(self, "Incorrect id_token: iat")
def validate_claims(self, id_token): utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) if 'nbf' in id_token and utc_timestamp < id_token['nbf']: raise AuthTokenError(self, 'Incorrect id_token: nbf') # Verify the token was issued in the last 10 minutes iat_leeway = self.setting('ID_TOKEN_MAX_AGE', self.ID_TOKEN_MAX_AGE) if utc_timestamp > id_token['iat'] + iat_leeway: raise AuthTokenError(self, 'Incorrect id_token: iat') # Validate the nonce to ensure the request was not modified nonce = id_token.get('nonce') if not nonce: raise AuthTokenError(self, 'Incorrect id_token: nonce') nonce_obj = self.get_nonce(nonce) if nonce_obj: self.remove_nonce(nonce_obj.id) else: raise AuthTokenError(self, 'Incorrect id_token: nonce')
def get_unauthorized_token(self): """Get unauthorized token from session passed on state parameter.""" unauthed_tokens = self.session.get('_utoken') if not unauthed_tokens: raise AuthTokenError(self, 'Missing unauthorized token') data_token = self.data.get(self.OAUTH_TOKEN_PARAMETER_NAME) if data_token is None: raise AuthTokenError(self, 'Missing unauthorized token') token = None utoken = unauthed_tokens orig_utoken = utoken if not isinstance(utoken, dict): utoken = parse_qs(utoken) if utoken.get(self.OAUTH_TOKEN_PARAMETER_NAME) == data_token: token = utoken else: raise AuthTokenError(self, 'Incorrect tokens') return token
def validate_and_return_id_token(self, id_token, access_token): """ Validates the id_token according to the steps at http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation. """ key = self.find_valid_key(id_token) if not key: raise AuthTokenError(self, "Signature verification failed") alg = key["alg"] rsa_key = jwk.construct(key) k = { "alg": rsa_key._algorithm, # pylint: disable=protected-access "kty": "oct", "k": base64.urlsafe_b64encode( rsa_key.prepared_key).rstrip(b"=").decode("utf-8"), } try: claims = jwt.decode( id_token, k, algorithms=[alg], audience=self.setting("KEY"), issuer=self.id_token_issuer(), options=self.JWT_DECODE_OPTIONS, ) except ExpiredSignatureError as error: raise AuthTokenError(self, "Signature has expired") from error except JWTClaimsError as error: raise AuthTokenError(self, str(error)) from error except JWTError as error: raise AuthTokenError(self, "Invalid signature") from error self.validate_claims(claims)
def validate_logout_claims(self, id_token: dict) -> None: """ Validated logout_token claims http://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken """ if id_token['iss'] != self.id_token_issuer(): raise AuthTokenError(self, 'Invalid issuer') client_id, _ = self.get_key_and_secret() if isinstance(id_token['aud'], str): id_token['aud'] = [id_token['aud']] if client_id not in id_token['aud']: raise AuthTokenError(self, 'Invalid audience') if len(id_token['aud']) > 1 and 'azp' not in id_token: raise AuthTokenError(self, 'Incorrect logout_token: azp') utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) if 'exp' in id_token and utc_timestamp > id_token['exp']: raise AuthTokenError(self, 'Signature has expired') # Verify the token was issued in the last 10 minutes iat_leeway = self.setting('ID_TOKEN_MAX_AGE', self.ID_TOKEN_MAX_AGE) if utc_timestamp > id_token['iat'] + iat_leeway: raise AuthTokenError(self, 'Incorrect logout_token: iat') if 'sid' not in id_token: raise AuthTokenError(self, 'Incorrect logout_token: sid')
def validate_and_return_logout_token( self, jws: str) -> dict: # noqa invalid-name """ Validated logout_token """ try: # Decode the JWT and raise an error if the sig is invalid id_token = JWS().verify_compact(jws.encode('utf-8'), self.get_jwks_keys()) except JWKESTException: raise AuthTokenError(self, 'Signature verification failed') self.validate_logout_claims(id_token) return id_token
def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') # get key id and algorithm key_id = get_unverified_header(id_token)['kid'] key = '' verify = os.environ.get('OAUTH2_VERIFY', False) try: # retrieve certificate for key_id if verify: certificate = self.get_certificate(key_id) key = certificate.public_key() options = {'verify_signature': verify} return jwt_decode( id_token, key=key, algorithms=['RS256'], audience=self.setting('KEY'), options=options, ) except (DecodeError, ExpiredSignatureError) as error: raise AuthTokenError(self, error)