def _decode_jwt_from_request(token_type, locations, fresh): # All the places we can get a JWT from in this request get_encoded_token_functions = [] # Get locations in the order specified by the decorator or JWT_TOKEN_LOCATION # configuration. if not locations: locations = config.token_location # Add the functions in the order specified by locations. for location in locations: if location == "cookies": get_encoded_token_functions.append( lambda: _decode_jwt_from_cookies(token_type)) if location == "query_string": get_encoded_token_functions.append(_decode_jwt_from_query_string) if location == "headers": get_encoded_token_functions.append(_decode_jwt_from_headers) if location == "json": get_encoded_token_functions.append( lambda: _decode_jwt_from_json(token_type)) # Try to find the token from one of these locations. It only needs to exist # in one place to be valid (not every location). errors = [] decoded_token = None jwt_header = None for get_encoded_token_function in get_encoded_token_functions: try: encoded_token, csrf_token = get_encoded_token_function() decoded_token = decode_token(encoded_token, csrf_token) jwt_header = get_unverified_jwt_headers(encoded_token) break except NoAuthorizationError as e: errors.append(str(e)) # Do some work to make a helpful and human readable error message if no # token was found in any of the expected locations. if not decoded_token: if len(locations) > 1: err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format( start_locs=", ".join(locations[:-1]), end_locs=locations[-1], details="; ".join(errors), ) raise NoAuthorizationError(err_msg) else: raise NoAuthorizationError(errors[0]) # Additional verifications provided by this extension verify_token_type(decoded_token, expected_type=token_type) if fresh: _verify_token_is_fresh(jwt_header, decoded_token) verify_token_not_blocklisted(jwt_header, decoded_token, token_type) custom_verification_for_token(jwt_header, decoded_token) return decoded_token, jwt_header
def check(self, header, token): # Check if token has already been no-listed. origtoken = token if 'jti' not in token and 'token' not in token: token = jwt_utils.decode_token(token) if DBSession.query(JWTNoList).filter( JWTNoList.token == token['jti']).count() > 0: return True return False
def _decode_jwt_from_request(request_type): # All the places we can get a JWT from in this request get_encoded_token_functions = [] locations = config.token_location # add the functions in the order specified in JWT_TOKEN_LOCATION for location in locations: if location == "cookies": get_encoded_token_functions.append( lambda: _decode_jwt_from_cookies(request_type)) if location == "query_string": get_encoded_token_functions.append(_decode_jwt_from_query_string) if location == "headers": get_encoded_token_functions.append(_decode_jwt_from_headers) if location == "json": get_encoded_token_functions.append( lambda: _decode_jwt_from_json(request_type)) # Try to find the token from one of these locations. It only needs to exist # in one place to be valid (not every location). errors = [] decoded_token = None jwt_header = None for get_encoded_token_function in get_encoded_token_functions: try: encoded_token, csrf_token = get_encoded_token_function() decoded_token = decode_token(encoded_token, csrf_token) jwt_header = get_unverified_jwt_headers(encoded_token) break except NoAuthorizationError as e: errors.append(str(e)) # Do some work to make a helpful and human readable error message if no # token was found in any of the expected locations. if not decoded_token: token_locations = config.token_location multiple_jwt_locations = len(token_locations) != 1 if multiple_jwt_locations: err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format( start_locs=", ".join(token_locations[:-1]), end_locs=token_locations[-1], details="; ".join(errors), ) raise NoAuthorizationError(err_msg) else: raise NoAuthorizationError(errors[0]) verify_token_type(decoded_token, expected_type=request_type) verify_token_not_blacklisted(decoded_token, request_type) return decoded_token, jwt_header
def refresh_token(): user = current_user access_token = create_access_token(user) refresh_token = create_refresh_token(user) jwt_data = decode_token(access_token) token = dict(access_token=access_token, refresh_token=refresh_token, issued_date=datetime.datetime.utcnow(), expiry_date=datetime.datetime.utcfromtimestamp( jwt_data.get('exp'))) return render_json(token)
def decorate(*args, **kwargs): resp = Unauthorized() resp.data = {"message": "Invalid token"} target_audit_uuid = "" if "audit_uuid" in kwargs: target_audit_uuid = kwargs["audit_uuid"] elif "scan_uuid" in kwargs: target_audit_uuid = kwargs["scan_uuid"][0:24] + "0" * 8 # Anonymous access configuration is allowed for non administration APIs only if app.config["ALLOW_ANONYMOUS_AUDIT_ACCESS"] is True and self.admin is False: identity = {"name": ""} else: try: # Check if given JWT is valid verify_jwt_in_request() identity = get_jwt_identity() # If the token is valid but already expired except ExpiredSignatureError: encoded_token, _ = _decode_jwt_from_headers() expired_token = decode_token(encoded_token, allow_expired=True) identity = expired_token["sub"] if "auth_endpoint" in identity: # Offer re-authorization endpoint for the target audit url = urlparse(identity["auth_endpoint"]) audit_qs = {"audit": target_audit_uuid} new_qs = {**parse_qs(url.query), **audit_qs} auth_endpoint = url._replace(query=urlencode(new_qs, doseq=True)).geturl() resp.data["reauth_endpoint"] = auth_endpoint raise resp except Exception: raise resp # Anonymous token is not allowed when anonymous access configuration is disabled if "name" not in identity or len(identity["name"]) == 0: raise resp # Check if the token's scope is valid for the specified API's target target_scopes = ["*"] if self.admin is False and len(target_audit_uuid) > 0: target_scopes.append(target_audit_uuid) if identity["scope"] not in target_scopes: raise resp g.identity = identity return f(*args, **kwargs)
def _decode_jwt_from_json(request_type): if request.content_type != "application/json": raise NoAuthorizationError("Invalid content-type. Must be application/json.") if request_type == "access": token_key = config.json_key else: token_key = config.refresh_json_key try: encoded_token = request.json.get(token_key, None) if not encoded_token: raise BadRequest() except BadRequest: raise NoAuthorizationError('Missing "{}" key in json data.'.format(token_key)) return decode_token(encoded_token)
def _decode_jwt_from_cookies(request_type): if request_type == 'access': cookie_key = config.access_cookie_name csrf_header_key = config.access_csrf_header_name else: cookie_key = config.refresh_cookie_name csrf_header_key = config.refresh_csrf_header_name encoded_token = request.cookies.get(cookie_key) if not encoded_token: raise NoAuthorizationError('Missing cookie "{}"'.format(cookie_key)) if config.csrf_protect and request.method in config.csrf_request_methods: csrf_value = request.headers.get(csrf_header_key, None) if not csrf_value: raise CSRFError("Missing CSRF token in headers") else: csrf_value = None return decode_token(encoded_token, csrf_value=csrf_value)
def auth(): auth_dict = request.get_json()['auth'] try: name = auth_dict['identity']['password']['user']['name'] password = auth_dict['identity']['password']['user']['password'] except Exception as e: response_dict = request.get_json() response_dict.update(e.messages) response = render_json(response_dict) response.status_code = 400 abort(response) user = models.User.objects(me.Q(username=name) | me.Q(email=name)).first() if user: if user.verify_password(password): access_token = create_access_token(user) refresh_token = create_refresh_token(user) jwt_data = decode_token(access_token) token = dict(methods=['password'], user=dict(id=user.id, name=name), access_token=access_token, refresh_token=refresh_token, issued_date=datetime.datetime.utcnow(), expiry_date=datetime.datetime.utcfromtimestamp( jwt_data.get('exp'))) return render_json(token) errors = [{ 'status': '401', 'title': 'User or Password mismatch', 'detail': 'User or Password mismatch' }] response_dict = request.get_json() response_dict['errors'] = errors response = render_json(response_dict) response.status_code = 401 abort(response)
def add(self, token): if 'token' in token: orig_token = copy.deepcopy(token['token']) token_type = token['token']['type'] decoded_token = token['token'] token = token['token']['jti'] elif 'type' in token: orig_token = copy.deepcopy(token) token_type = token['type'] decoded_token = token token = token['jti'] else: orig_token = copy.deepcopy(token) decoded_token = jwt_utils.decode_token(token) token_type = decoded_token['type'] token = decoded_token['jti'] if self.check('x', decoded_token): return # Already restricted. if DBSession.query(JWTNoList).filter(JWTNoList.token == token).first(): return if token_type == 'access': remove_after = datetime.utcnow( ) + app.config['JWT_ACCESS_TOKEN_EXPIRES'] else: remove_after = datetime.utcnow( ) + app.config['JWT_REFRESH_TOKEN_EXPIRES'] with ScopedSession() as local_db_session: TokenNo = JWTNoList( token=token, token_type=token_type, remove_after=remove_after, ) local_db_session.add(TokenNo)
def is_accessible(self): try: token = request.args.get("jwt") if not token: token = urllib.parse.parse_qsl(request.args.get("url"))[0][1] decoded_token = decode_token(token) verify_token_not_blacklisted(decoded_token, request_type="access") ctx_stack.top.jwt = decoded_token if has_user_loader(): user = user_loader(ctx_stack.top.jwt["identity"]) if user is None: raise UserLoadError( "user_loader returned None for {}".format(user)) ctx_stack.top.jwt_user = user current_user = get_jwt_identity() is_admin = UserModel.query.filter_by( username=current_user).one().admin return current_user and is_admin except Exception as e: current_app.logger.critical("FAULTY ADMIN UI ACCESS: %s", str(e)) return False
def _decode_jwt_from_headers(): header_name = config.header_name header_type = config.header_type # Verify we have the auth header jwt_header = request.headers.get(header_name, None) if not jwt_header: raise NoAuthorizationError("Missing {} Header".format(header_name)) # Make sure the header is in a valid format that we are expecting, ie # <HeaderName>: <HeaderType(optional)> <JWT> parts = jwt_header.split() if not header_type: if len(parts) != 1: msg = "Bad {} header. Expected value '<JWT>'".format(header_name) raise InvalidHeaderError(msg) encoded_token = parts[0] else: if parts[0] != header_type or len(parts) != 2: msg = "Bad {} header. Expected value '{} <JWT>'".format(header_name, header_type) raise InvalidHeaderError(msg) encoded_token = parts[1] return decode_token(encoded_token)
def _decode_jwt_from_request( locations: LocationType, fresh: bool, refresh: bool = False, verify_type: bool = True, ) -> Tuple[dict, dict, str]: # Figure out what locations to look for the JWT in this request if isinstance(locations, str): locations = [locations] if not locations: locations = config.token_location # Get the decode functions in the order specified by locations. # Each entry in this list is a tuple (<location>, <encoded-token-function>) get_encoded_token_functions = [] for location in locations: if location == "cookies": get_encoded_token_functions.append( (location, lambda: _decode_jwt_from_cookies(refresh))) elif location == "query_string": get_encoded_token_functions.append( (location, _decode_jwt_from_query_string)) elif location == "headers": get_encoded_token_functions.append( (location, _decode_jwt_from_headers)) elif location == "json": get_encoded_token_functions.append( (location, lambda: _decode_jwt_from_json(refresh))) else: raise RuntimeError(f"'{location}' is not a valid location") # Try to find the token from one of these locations. It only needs to exist # in one place to be valid (not every location). errors = [] decoded_token = None jwt_header = None jwt_location = None for location, get_encoded_token_function in get_encoded_token_functions: try: encoded_token, csrf_token = get_encoded_token_function() decoded_token = decode_token(encoded_token, csrf_token) jwt_location = location jwt_header = get_unverified_jwt_headers(encoded_token) break except NoAuthorizationError as e: errors.append(str(e)) # Do some work to make a helpful and human readable error message if no # token was found in any of the expected locations. if not decoded_token: if len(locations) > 1: err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format( start_locs=", ".join(locations[:-1]), end_locs=locations[-1], details="; ".join(errors), ) raise NoAuthorizationError(err_msg) else: raise NoAuthorizationError(errors[0]) # Additional verifications provided by this extension if verify_type: verify_token_type(decoded_token, refresh) if fresh: _verify_token_is_fresh(jwt_header, decoded_token) verify_token_not_blocklisted(jwt_header, decoded_token) custom_verification_for_token(jwt_header, decoded_token) return decoded_token, jwt_header, jwt_location
def decode_jwt_from_request(request_type, **options): locations = options.get("locations", []) # All the places we can get a JWT from in this request get_encoded_token_functions = [] if locations: if "cookies" in locations: get_encoded_token_functions.append( lambda: _decode_jwt_from_cookies(request_type)) if "query_string" in locations: get_encoded_token_functions.append(_decode_jwt_from_query_string) if "headers" in locations: get_encoded_token_functions.append(_decode_jwt_from_headers) if "json" in locations: get_encoded_token_functions.append( lambda: _decode_jwt_from_json(request_type)) else: if config.jwt_in_cookies: get_encoded_token_functions.append( lambda: _decode_jwt_from_cookies(request_type)) if config.jwt_in_query_string: get_encoded_token_functions.append(_decode_jwt_from_query_string) if config.jwt_in_headers: get_encoded_token_functions.append(_decode_jwt_from_headers) if config.jwt_in_json: get_encoded_token_functions.append( lambda: _decode_jwt_from_json(request_type)) # Try to find the token from one of these locations. It only needs to exist # in one place to be valid (not every location). errors = [] decoded_token = None for get_encoded_token_function in get_encoded_token_functions: try: encoded_token, csrf_token = get_encoded_token_function() decoded_token = decode_token(encoded_token, csrf_token) break except ExpiredSignatureError: expired_data = decode_token(encoded_token, csrf_token, allow_expired=True) if options.get("allow_expired", False): decoded_token = expired_data else: # Save the expired token so # we can access it in a callback later ctx_stack.top.expired_jwt = expired_data raise except NoAuthorizationError as e: errors.append(str(e)) # Do some work to make a helpful and human readable error message if no # token was found in any of the expected locations. if not decoded_token: token_locations = config.token_location multiple_jwt_locations = len(token_locations) != 1 if multiple_jwt_locations: err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})"\ .format( start_locs=", ".join(token_locations[:-1]), end_locs=token_locations[-1], details="; ".join(errors) ) raise NoAuthorizationError(err_msg) else: raise NoAuthorizationError(errors[0]) verify_token_type(decoded_token, expected_type=request_type) verify_token_not_blacklisted(decoded_token, request_type) return decoded_token