def get_token_auth_header(): auth = request.headers.get('Authorization') if not auth: raise AuthError({ 'code': 'authorization_header_missing', 'description': 'Authorization header is expected.' }, 401) parts = auth.split() if parts[0].lower() != 'bearer': raise AuthError({ 'code': 'invalid_header', 'description': 'Authorization header must start with "Bearer".' }, 401) elif len(parts) == 1: raise AuthError({ 'code': 'invalid_header', 'description': 'Token not found.' }, 401) elif len(parts) > 2: raise AuthError({ 'code': 'invalid_header', 'description': 'Authorization header must be bearer token.' }, 401) token = parts[1] return token
def _check_permissions(permission, payload): if 'permissions' not in payload: raise AuthError( { 'code': 'invalid_claims', 'description': 'Permissions not included in JWT' }, 400) # For unit testing purposes # The key-value pair of 'test_permission' is included in the request # headers when the request is identified as an unit test request; # permissions of the routes then can be tested via the manipulations of # the permissions list in the payload if 'test_permission' in request.headers: payload['permissions'] = [request.headers['test_permission']] if isinstance(permission, list) and len(permission) > 1: if not set(permission).intersection(payload['permissions']): raise AuthError( { 'code': 'unauthorized', 'description': 'Permission not found' }, 401) else: if permission not in payload['permissions']: raise AuthError( { 'code': 'unauthorized', 'description': 'Permission not found' }, 401) return True
def verify_decode_jwt(token): jsonurl = urlopen(f'https://{AUTH0_DOMAIN}/.well-known/jwks.json') jwks = json.loads(jsonurl.read()) unverified_header = jwt.get_unverified_header(token) rsa_key = {} if 'kid' not in unverified_header: raise AuthError( { 'code': 'invalid_header', 'description': 'Authorization malformed.' }, 401) 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'] } if not rsa_key: raise AuthError( { 'code': 'invalid_header', 'description': 'Unable to find the appropriate key.' }, 400) try: payload = jwt.decode(token, rsa_key, algorithms=ALGORITHMS, audience=API_AUDIENCE, issuer='https://' + AUTH0_DOMAIN + '/') return payload except jwt.ExpiredSignatureError: raise AuthError( { 'code': 'token_expired', 'description': 'Token expired.' }, 401) except jwt.JWTClaimsError: raise AuthError( { 'code': 'invalid_claims', 'description': ('Incorrect claims. Please, check the audience and issuer.') }, 401) except Exception: raise AuthError( { 'code': 'invalid_header', 'description': 'Unable to parse authentication token.' }, 400)
def check_permissions(permission, payload): permissions = payload.get("permissions") if permissions is None: raise AuthError({ "code": "invalid_payload", "description": "permission missing in payload" }, 401) if permission not in permissions: raise AuthError({ "code": "invalid_payload", "description": "Permission not found." }, 401) return True
def check_permissions(permission, payload): if 'permissions' not in payload: raise AuthError( { 'code': 'invalid_claims', 'description': 'Permissions not included in JWT.' }, 400) if permission not in payload['permissions']: raise AuthError( { 'code': 'unauthorized', 'description': 'Permission not found.' }, 403) return True
def wrapper(*args, **kwargs): token = _get_token_auth_header() try: payload = _verify_decode_jwt(token) except BaseException: raise AuthError( { "code": "jwt_decode_error", "description": "Error decoding JWT" }, 401) _check_permissions(permission, payload) return f(payload, *args, **kwargs)
def validate_response(response): """validate_response """ error_suffix = " response={!r}".format(response) if response.status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN): raise AuthError("operation=auth_error," + error_suffix, response) if response.status_code == status.HTTP_404_NOT_FOUND: raise NotFoundError("operation=not_found_error," + error_suffix, response) if status.is_client_error(code=response.status_code): raise ClientError("operation=client_error," + error_suffix, response) if status.is_server_error(code=response.status_code): raise ServerError("operation=server_error," + error_suffix, response)
def verify_decode_jwt(token): jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json") jwks = json.loads(jsonurl.read()) unverified_header = jwt.get_unverified_header(token) 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"] } if rsa_key: try: payload = jwt.decode( token, rsa_key, algorithms=ALGORITHMS, audience=API_AUDIENCE, issuer="https://"+AUTH0_DOMAIN+"/" ) return payload except jwt.ExpiredSignatureError: raise AuthError("token is expired", 401) except jwt.JWTClaimsError: raise AuthError("invalid_claims", 401) except Exception as e: raise AuthError({"code": "invalid_header", "description": "Unable to parse authentication" " token."}, 401) raise AuthError("Unable to find appropriate key", 401)
def _get_token_auth_header(): ############################################################# # # TODO This is not the correct way to implement security, # I'd imagine that one should be passing the access key # between the front end and back end through the request # header. However, I have spend two weeks trying different # ways and still couldn't come up with a feasible solution. # Since this part is not critical for the capstone, I have # implemented this temporary solution to force the # application to work, and I will try to figure it out in # a later time. # # This implementation does not grab the access token from # the request header, but check the host url from the request # header and see if it's the same as the 'API Audience' set # up using the Auth0 API UI. If it is, grab the access key # stored in the Flask session when the application is going # through the callback code after logging in. And all the # steps after that follow strictly to how we are taught. # ############################################################### if request.headers['HOST'] in api_audience: # Postman test work-around if 'POSTMAN_TOKEN' not in request.headers: # Unit test work-around if 'test_permission' in request.headers: return test_token if conf_access_key in session: access_key = session[conf_access_key] return access_key['access'] else: raise AuthError( { "code": "authorization_required", "description": "User is not logged in" }, 401) if 'Authorization' not in request.headers: raise AuthError( { "code": "authorization_required", "description": "Authorization is expected in header" }, 401) # "Authorization": "bearer <<token>>" auth_header = request.headers['Authorization'] header_parts = auth_header.split(' ') if len(header_parts) != 2 or header_parts[0].lower() != 'bearer': raise AuthError( { "code": "invalid_token", "description": "Mendatory authorization requires 'bearer' token" }, 401) return header_parts[1]
def _verify_decode_jwt(token): # Get the public key from Auth0 jsonurl = urlopen(f'https://{auth0_domain}/.well-known/jwks.json') jwks = json.loads(jsonurl.read()) # Get the data in the header unverified_header = jwt.get_unverified_header(token) # Choose the key rsa_key = {} if 'kid' not in unverified_header: raise AuthError( { 'code': 'invalid_header', 'description': 'Authorization malformed' }, 401) 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'] } # Finally, verify!!! if rsa_key: try: # USE THE KEY TO VALIDATE THE JWT payload = jwt.decode(token, rsa_key, algorithms=algorithms, audience=api_audience, issuer=(f'https://{auth0_domain}/')) return payload except jwt.ExpiredSignatureError: raise AuthError( { 'code': 'token_expired', 'description': 'Token expired' }, 401) except jwt.JWTClaimsError: raise AuthError( { 'code': 'invalid_claims', 'description': 'Incorrect claims. Please check the audience and issuer' }, 401) except Exception: raise AuthError( { 'code': 'invalid_header', 'description': 'Unable to parse authentication token' }, 400) raise AuthError( { 'code': 'invalid_header', 'description': 'Unable to find the appropriate key' }, 400)