As mentioned we were planning to separate tenants by applications and therefore would validate that JWT is valid, domain matches and application_id matches. The frontend framework auth0-spa however requires an audience to produce a full JWT. The documentation suggests that the value for audience should be the id of an API created in the dashboard. We don’t see a way of associating an API with an application or a user pool so it seems like a fully independent entity. Because of that, it seems like an arbitrary requirement and we would simply have a global API for all tenants just to satisfy the requirement.
a. Is this correct?
b. Is there some relevance to the API/audience that we don’t see and if yes, can we associate the API somehow with the userpool and the application to make it meaningful for us?
2. Validating the application_id
Our backend service is developed in python using FastAPI. Because of that we are basing our validation logic on this tutorial (and modifying it to be compatible with FastAPI).
This snippet contains logic to validate that the JWT belongs to the correct tenant:
token = get_token_auth_header()
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=GLOBAL_API_AUDIENCE_ID,
issuer="https://" + AUTH0_DOMAIN + "/"
)
# manually check if `azp` which holds the application id matches the application_id that is configured in the tenants instance
if payload["azp"] != TENANT_APPLICATION_ID: # (this is the application id)":
raise AuthApiError("you are trying to access another tenants application")
a. Is this the correct way to do this?
b. Is there maybe a designated way to solve this and maybe even a trusted framework rather than these code snippets?
The audience should reflect the consumer of the token. Your SPA should not consume access tokens. Access tokens should be sent from your SPA (the client) to your backend API (the audience/resource) to verify the request between the them.
Maybe @jesstemporal can help with this. Do we have any FastAPI auth frameworks we recommend?
Hi @dan.woda thanks for the quick response. There may have been a misunderstanding. To clarify we are planning to do the following
use auth0-spa in SPA to obtain token
send token to backend as part of request
for every request → validate token using the python logic above (in the backend)
We are not planning to consume the token in the frontend (SPA) but in order to pass it to the backend we need to read it in the frontend, right?
The audience should reflect the consumer of the token
Based on our findings we would have a single API to represent the same type of service across all tenants. Is there any benefit of having a dedicated API/audience per tenant, given that it is not tied to a user pool anyway?
… to your backend API (the audience/resource) to verify the request between the them.
Is the verification by manually checking the “azp” key as outlined above appropriate? (asking because this is not taken from documentation or example and we just came up with it)
The code above contains the check which is our proposal of answering the question “does this token belong to the tenant that owns this service instance?” This check would run for every request coming into our backend service instances.
Since this is not a designated usage of Auth0 we would like to know:
Do you see any concerns with this?
Is there a designated way to do this that we missed?
This claim is meant to indicate what application was issued the ID token. It isn’t a strict indication of which organization the user belongs to, but it seems to get the job done.