Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Auth0 to Authorize API requests from both our SPA and our other back-end services

We have a Single Page App which calls our back-end services (C# and Python), authorizing these requests using the Auth0 SPA flow.

Our back end services only make requests to each other as part of processing a request from an SPA user, so currently we just forward the Authorization header from the SPA request, using it to authorize the operation on each called service.

I now want to add back-end processing jobs that will make requests between our services, making calls to the existing endpoints. These requests will will not have any existing auth header to forward, so will need to construct their own.

Based on the Auth0 docs, here and here, I believe I ought to authorize these requests using a Client Credentials Grant, and I infer that our existing endpoints will therefore need to accept requests authorized in two different ways: from the SPA or from other services.

The problem is that JWTs from the SPA are signed using one secret key, while those from services are signed using their respective secret keys. Am I right that I need to configure my endpoints so that they will accept JWTs constructed using any of these secret keys?

So my core question is: Is this understanding correct, or should I be doing it a different way altogether?

Details:

The two types of Authorization JWT tokens that our endpoints will need to handle are:

1 Requests from our SPA, containing:

sub: SPA-USER-ID
aud: SPA-CLIENT-ID

signed using HS256 (for historical reasons) using our SPA's client secret.

2 Service-to-service requests, containing:

sub: SERVICE-ID@clients
aud: API-ID
scope: ""

signed using HS256 (for now to keep things simple) using our calling service's client secret.

If one endpoint is going to decode and verify both requests, firstly it will fail because 'aud' values are different. I think the 'aud' value of our existing SPA calls is an error - it ought to be the ID of the API receiving the request. Then the 'aud' values in both requests will be the same.

The next difference is that they are each signed with a different secret key - either the SPA's or the calling service's (and potentially with a different algorithm too, if I chose to make the new service calls using RS256 as Auth0 recommends.)

I can't spot obvious ways to modify the Python and C# quickstarts to accept tokens encoded with different keys. I'm thinking about coding it myself, to manually try one, and if it fails then try the other. I'm confident I can do it for the Python endpoint authorization, which I wrote myself based on the Auth0 quickstart using PyJWT, but less familiar with our C# stuff, which is also based on an Auth0 quickstart but seem to use some built-in .NET JWT validation middleware, and I'm not at all sure I can get at its innards to add the above functionality.

But if I'm doing this the right way, then surely this is a common requirement?

Apologies if this question is badly-formed, I'm know nothing about auth, and am reading the Auth0/Oath2 docs to figure it out as I go.

like image 965
Jonathan Hartley Avatar asked Oct 18 '22 12:10

Jonathan Hartley


1 Answers

(Answering my own question, in the third person)

Is this understanding correct, or should I be doing it a different way altogether?

The overall flow outlined in the question is correct. However there is one pervasive misunderstanding:

When services (or the SPA) request new access tokens from Auth0 (or any issuer), the secret key they provide is not usually the one that Auth0 uses to sign the returned token. Instead, the issuer uses the secret key of the audience (the API that will be called using this access token.)

The reason the OP got confused about this is because the code he inherited, as noted in the question, incorrectly passes an 'audience' value of the SPA itself when requesting tokens. Hence the resulting tokens are signed using the SPA's secret. Once this is fixed, to use the API ID as the 'audience', then the tokens generated for the SPA and those generated for service-to-service calls will all:

  1. contain audience=API-ID, and hence will contain equivalent payloads in all regards except for 'sub' (user id) and 'scope' (currently unused.)

  2. be signed with HS256 using the API-SECRET of the audience.

Hence, an endpoint doesn't have to check two different types of tokens encoded using two different secret keys. It can just check incoming requests using it's own 'audience' identifier, and its own secret key, and this will successfully decode and validate requests from both the SPA and from other services.

like image 109
Jonathan Hartley Avatar answered Oct 21 '22 06:10

Jonathan Hartley