# Application per tenant - role of API/audience

**URL:** <https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534>\
**Category:** Get Help\
**Created:** [July 21, 2021, 3:56pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534 "2021-07-21T15:56:53Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbee](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/mbee/32/12776_2.png) [@mbee](https://community.auth0.com/u/mbee)\
**Post date:** [July 21, 2021, 3:56pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/1 "2021-07-21T15:56:54Z")

</div>

We are trying to figure out how to properly do tenant isolation using auth0 for our multi-tenant SaaS application.

We determined that it’s best for us to [isolate tenants through individual applications per tenant](https://auth0.com/docs/best-practices/multi-tenant-apps-best-practices). While implementing this, a few questions arose.

**1. The role of the audience and APIs**

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](https://community.auth0.com/t/gettokensilently-returns-a-32-character-string-not-jwt/34294). 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](https://auth0.com/docs/quickstart/backend/python/01-authorization) (and modifying it to be compatible with FastAPI).

This snippet contains logic to validate that the JWT belongs to the correct tenant:

```auto
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?

---

<div class="post-metadata">

**Author:** ![dan.woda](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/dan.woda/32/18340_2.png) [@dan.woda](https://community.auth0.com/u/dan.woda)\
**Post date:** [July 21, 2021, 8:07pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/3 "2021-07-21T20:07:25Z")

</div>

> [@mbee](#):
>
> **1. The role of the audience and APIs**
> 
> 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](https://community.auth0.com/t/gettokensilently-returns-a-32-character-string-not-jwt/34294). 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?

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.

> [@mbee](#):
>
> **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](https://auth0.com/docs/quickstart/backend/python/01-authorization?_ga=2.177003813.1290533317.1626708324-1261373470.1602622721&_gl=1*1q7erow*rollup_ga*MTI2MTM3MzQ3MC4xNjAyNjIyNzIx*rollup_ga_F1G3E656YZ*MTYyNjg5NDUzNC4xMTYuMS4xNjI2ODk2MjcyLjMx) (and modifying it to be compatible with FastAPI).
> 
> 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?
> 
> This snippet contains logic to validate that the JWT belongs to the correct tenant:

Maybe @jesstemporal can help with this. Do we have any FastAPI auth frameworks we recommend?

---

<div class="post-metadata">

**Author:** ![mbee](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/mbee/32/12776_2.png) [@mbee](https://community.auth0.com/u/mbee)\
**Post date:** [July 21, 2021, 8:21pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/4 "2021-07-21T20:21:03Z")

</div>

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)

Thanks for your support.

---

<div class="post-metadata">

**Author:** ![dan.woda](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/dan.woda/32/18340_2.png) [@dan.woda](https://community.auth0.com/u/dan.woda)\
**Post date:** [July 22, 2021, 4:08pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/5 "2021-07-22T16:08:55Z")

</div>

> [@mbee](#):
>
> but in order to pass it to the backend we need to read it in the frontend, right?

The ID token returned in the request should have the user information necessary for your SPA. [This topic adds some detail if you’re interested in learning more](https://community.auth0.com/t/clarification-on-token-usage/8447).

> [@mbee](#):
>
> 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?

You can use a single API to serve all of your applications.

> [@mbee](#):
>
> 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)

Can you give an example of the resources you’re securing with this claim. I am going to ask our field team for their input.

---

<div class="post-metadata">

**Author:** ![mbee](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/mbee/32/12776_2.png) [@mbee](https://community.auth0.com/u/mbee)\
**Post date:** [July 23, 2021, 3:11pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/6 "2021-07-23T15:11:59Z")

</div>

> [@mbee](#):
>
> ```auto
> 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")
> 
> ```

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?

---

<div class="post-metadata">

**Author:** ![dan.woda](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/dan.woda/32/18340_2.png) [@dan.woda](https://community.auth0.com/u/dan.woda)\
**Post date:** [July 23, 2021, 8:37pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/7 "2021-07-23T20:37:48Z")

</div>

> [@mbee](#):
>
> Do you see any concerns with this?

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.

As for other ways to do this, you could [store the tenant/organization in the user’s app\_metadata](https://auth0.com/docs/users/metadata) then [Create Custom Claims](https://auth0.com/docs/tokens/create-namespaced-custom-claims) in an action or rule to indicate the user’s tenant/organization.

Similarly, you could use a custom claim to indicate which application the user is calling from, although this is similar to using the azp claim.

You could use [Auth0 Organizations](https://auth0.com/docs/organizations), although this may be a bit different than your requirements, and requires an enterprise subscription.

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/auth0/original/3X/b/5/b560f266932fcce5d57f09a6595bd50518cae3f3.jpeg) [@system](https://community.auth0.com/u/system)\
**Post date:** [August 23, 2021, 4:25pm UTC](https://community.auth0.com/t/application-per-tenant-role-of-api-audience/66534/8 "2021-08-23T16:25:47Z")

</div>

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.
