I have setup an auth0 single web page application and an API which I use in Angular and FastAPI respectively.
After I login via the Angular web app, a GET request is sent to FastAPI which fails mentioning 400 Bad request. On the browser console, the response error is “invalid issuer” but the api audience is the one on my auth0 dashboard.
When I use Postman to make the request, the message mentions “invalid payload padding”.
This is the code used to verify the token:
token_auth_schema = HTTPBearer()
def token_auth(token: str = Depends(token_auth_schema)):
result = VerifyToken(token.credentials).verify()
if result.get("status"):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=result)
return result
class VerifyToken:
"""Does all the token verification using PyJWT"""
def __init__(self, token, permissions=None, scopes=None):
self.token = token
self.permissions = permissions
self.scopes = scopes
self.config = set_up()
# This gets the JWKS from a given URL and does processing so you can use any of
# the keys available
jwks_url = f'https://{self.config["DOMAIN"]}/.well-known/jwks.json'
self.jwks_client = jwt.PyJWKClient(jwks_url)
def verify(self):
# This gets the 'kid' from the passed token
try:
self.signing_key = self.jwks_client.get_signing_key_from_jwt(self.token).key
except jwt.exceptions.PyJWKClientError as error:
return {"status": "error", "msg": f"PyJWKClientError: {str(error)}"}
except jwt.exceptions.DecodeError as error:
return {"status": "error", "msg": f"DecodeError: {str(error)}"}
try:
payload = jwt.decode(
self.token,
self.signing_key,
algorithms=self.config["ALGORITHMS"],
audience=self.config["API_AUDIENCE"],
issuer="https://"+self.config["DOMAIN"],
)
except Exception as e:
return {"status": "error", "message": str(e)}
if self.scopes:
result = self._check_claims(payload, "scope", str, self.scopes.split(" "))
if result.get("error"):
return result
if self.permissions:
result = self._check_claims(payload, "permissions", list, self.permissions)
if result.get("error"):
return result
return payload
def _check_claims(self, payload, claim_name, claim_type, expected_value):
instance_check = isinstance(payload[claim_name], claim_type)
result = {"status": "success", "status_code": 200}
payload_claim = payload[claim_name]
if claim_name not in payload or not instance_check:
result["status"] = "error"
result["status_code"] = 400
result["code"] = f"missing_{claim_name}"
result["msg"] = f"No claim '{claim_name}' found in token."
return result
if claim_name == "scope":
payload_claim = payload[claim_name].split(" ")
for value in expected_value:
if value not in payload_claim:
result["status"] = "error"
result["status_code"] = 403
result["code"] = f"insufficient_{claim_name}"
result["msg"] = (
f"Insufficient {claim_name} ({value}). You don't have "
"access to this resource"
)
return result
return result