How do I validate a token via Web API Access?

We have an API, with a non interactive client for each of our tenants, if I go to the API test tab and get a token for a client, I assume this is a JWT token that we can validate when 3rd parties call our API. I can use the token via Postman to call our api but I need to validate the token against the tenant.

I have created an AuthorisationHandler to do this (with attribute on API controller) and trying to validate the JWT token, I am using the Jose.JWT library as mentioned somewhere in the docs and when I have the token via the header, and the clientsecret we store on our side, the trying to validate the token like this:

var json = Jose.JWT.Decode(token, Encoding.UTF8.GetBytes(secretKey), JwsAlgorithm.RS256);

I can’t get the above working, I am not too clear on what format these values need to be string or byte arrays and/or base 64 encoded/decoded. Is this the correct way to validate a token and is the correct approach validating tokens via an API?

UPDATE:
I may be confused… I am creating a non-interactive client where my customers get the secret key, do they need to call Auth0 directly to get a token then call our API (similar to the test token on test tab in the API section) I think I am assuming our customers generate a token the same as we create in the standard web application which I now believe is HS256, which is incorrect? Should our customers call Auth0 to get a token. like the test token, directly or should we provide this via our API (proxy where they use their clientSecret and we call Auth0) what is usual practice here?

The situation is that I will have multiple customers calling our API, using their own client credentials, however I need to validate each one of our tenants, I think I was doing too much in trying to get the access token and validate this using their clientSecret on the fly? This is why I am trying to validate the token. so how to I validate the HS256 or indeed the client to ensure our tenant is correct.

The key thing there is not just validating whether the token is valid, but I need to know who is the client is. I can check the claims “sub” Subject to check the ClientId matches the client Id associated with our tenant, however I can’t be sure that someone has generated a token using their secret and just adding in another clientId if they got hold of it? so think I need to validate the token against the secret key that we hold to ensure the tenant is in fact the tenant? Something seems to be missing, something I can’t see?

From a quick look at the library repository the second parameter to Decode in this situation should be a byte array so your use of Encoding.UTF8.GetBytes(secretKey) seems adequate.

However, if this is an HS256 signed access token which would indeed be validated by providing the API signing secret (you mentioned client secret, so if that was not a typo, be sure to use the API secret) then you should be passing the third parameter as Jose.JwsAlgorithm.HS256 and not the RS256 one.


Update:

If you configure an API in Auth0 then the token validation procedure you need to implement in the API will have to do the following in terms of signature (other checks after the signature check should still occur):

  • validate the signature using the public key associated with your Auth0 tenant/domain if the API is configured to use RS256.
  • validate the signature using the API signing secret available in its settings if the API is configured to use HS256.

With RS256 the only party able to issue tokens is the Auth0 tenant/domain while with HS256 the API itself can also in theory issue tokens to itself as the signing key is symmetric and the API also possess that information. It’s important that with HS256 the API signing secret is not known to anyone else, otherwise those parties will be able to issue tokens.

If you exclude the technical detail that with HS256 the API can also issue tokens for itself, the only party issuing tokens will be the Auth0 tenant/domain. Furthermore, these access tokens can be issued through:

  • end-user based flows - an end-user authenticates into your tenant/domain and the client application requests an access token for your API so that it can perform calls to the API on behalf of that user. Currently, in this situation the access token will be a JWT and the sub claim will contain the user identifier.
  • client credentials flow - there is no end-user involved, but a client application able to perform client authentication (through the use of client identifier and client secret) was authorized to obtain access tokens for the API. In this case, the issued access token will also be a JWT, but the sub claim at this time is associated with the client application itself.

In both cases above, the API validates the received access token mostly in the same way although in one the information contained within is associated to an end-user and in the other is associated directly to a client application.

In conclusion, if you want the API to accept a single method of authentication and that method is bearer access tokens issued by Auth0 then the third-party client application will have to perform a client credentials grant (using their client identifier and secret) against your Auth0 tenant/domain and then use the issued access token to call into the relevant API. In general, the client application should perform the grant directly with your Auth0 tenant/domain. You can also just issue API keys to each of your tenant and bypass Auth0 completely, however, the API could now possibly need to handle multiple authentication methods if it happens to also be called through end-user based flows that require the more complex OAuth2 flows.

In the case of client credentials, given the issued access token can only be signed by the tenant/domain (RS256 case) or by the tenant/domain and API itself (HS256 case using API signing secret) you can use the information contained within the token (client identifier) to know which third-party client application is behind the call. On the other hand, if the third-party client application used their client secret to manually generate HS256 tokens you would not be able to trust the information within because those client applications are under the control of third-parties.

my comment is not submitting…

long comments are not submitting, (below the limit)

I have updated the question as the long comment didn’t work here.

I updated the answer with some additional information.

ok we have a non-interactive client and supply the client and secret to our customer, we could have an endpoint where the customer uses their secret key to get a JWT token (e.g. our from own managementAPI) which we generate via Auth0. The customer will then use that to make calls to our API. I assume they will use that JWT (which is RS256) to make calls until they get a 401, in which case they request a new token from our API. It seems that only if we create the JWT for the customer they can’t spoof the “sub” subject? have read the documentation numerous times and am just struggling to get it

In this case should i use another random API key rather than the secret and not supply the secret key to the customer? It just need to know and then validate which tenant is actually connecting via one of multiple non-interactive clients. in the car eof RS256, is it the non-interactive client secret that signs the JWT or is the Auth0 client secret?

If the client obtains the token through a client credentials grant for your API (either through a process brokered by your own Management API) or directly at /oauth/token then they won’t be able to spoof any claims as that token will be signed by a secret that only the Auth0 tenant knows (RS256) or only the Auth0 tenant and your API knows (HS256). In essence the client identifier and secret issued to the third-party is only used for client authentication; not to sign tokens.

You could indeed consider just issuing opaque API keys to the third-parties and bypass the OAuth2 flows available in your tenant. It decreases the complexity for third-parties being able to call the API, but it may increase the complexity of the API itself because it now has to manage an API keys implementation.