Using Gravitee API Manager with Auth0 as an authorisation server

Valentin
Everysens
Published in
10 min readApr 13, 2021

--

In an effort to centralize, document, secure and monitor its offer of APIs, Everysens is deploying an API Manager solution this year.

For this purpose, we have chosen to use Gravitee. Additionally we are using Auth0 as a Authorization Manager for all of our applications.

Auth0.com
Gravitee.io

This article will describe the steps to take to quickly configure Auth0 and Gravitee to work together. Gravitee will provide the API gateway where all API calls will pass through. Auth0 will provide the JWT tokens to authorize the API calls.

Please note that describing how OAuth 2.0 works in general is out of the scope of this article, because there are already plenty of very good resources on the subject.

We won’t either describe how to integrate your applications with Auth0, we expect this step to be already performed. We will only describe here how to configure the API Manager layer that Gravitee provides and secure it with JWT tokens provided by Auth0.

Finally because this is a work in progress, some information such as API URLs and documentation may be redacted in this post.

Let’s start with the setup, starting on the Auth0 side !

Configuring applications on Auth0

At Everysens we have different kinds of client applications that need to talk to our APIs :

  • First party SPA applications (Single Page Application), for example our own frontend applications designed for our end-users. This kind of client application requests OAuth 2.0 tokens involving a user interaction, thus, most of the time, using the grant_type authorization_code
  • First party M2M applications (Machine to Machine), for example our own backend applications that need to call our own APIs. This kind of client application may request OAuth 2.0 tokens for themselves, thus using the grant_type client_credentials
  • Third Party applications, for example our customer’s frontend or backend applications who need to call our APIs. This kind of client may request tokens using any grant_type depending on the situation

In the following we will demonstrate the setup for the M2M and SPA applications only. The other variants you may be using should work very similarly.

First, select one of your existing M2M or SPA application on the admin dashboard in Auth0 or create a new one and integrate it in your application following the documentation given by Auth0. Again, we won’t go into details on how this integration should be implemented because it really depends on your use case and is out of the scope of this article. The starting point is where you already have a application of yours integrated with Auth0.

Take note of their client_id because we’ll need them in the next steps. In our case, these are :

  • M2M application : tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace
  • SPA application : VwJOqbIgb4OVBVYs0YYoUqre7b979nKl

Configuring an API on Gravitee

Once you have your gravitee stack setup (SaaS or on-premise will do), head to the API section and create an API for your application currently protected by Auth0 tokens. In my case I used the Import wizard with one of my Swagger files.

Create a new API

Make sure to check Create policies on paths to instruct Gravitee to prefill every endpoint it finds in the swagger file.

Import from Swagger file

From the Proxy > Endpoints section of your API, make sure to configure the default endpoint to point to where your API is actually served :

Configure the endpoints section

Now head to the Plans sections to create a JWT plan :

Create a new Plan with a meaningful name

Then, configure the authentication section :

  • select the JWT Authentication type
  • Select JWKS_URL as the resolver
  • Fill in the jwks URL of your Auth0 tenant in the Resolver parameter field. This contains, among others, the public keys which can be used to validate the Auth0 token from the incoming request
    That URL should be in the form https://your-auth0-tenant.auth0.com/.well-known/jwks.json and can be found in the Endpoints section in the Advanced Settings of any of your Auth0 applications.
FInd the jwks URL of your Auth0 tenant
Configure JWT Authentication

Then save your plan and publish it from the Staging state to the Published state.

Don’t forget to START THE API to make it available on the Gravitee Gateway, else you’ll have a 404 error later on :

Don’t forget to START the API

If you have an orange bar on top saying your API is out of sync on the gateway, don’t forget to click on deploy :

Make sure to click on the deploy button if you see this warning

That’s it, your API is now set up, we’ll then create an Application in Gravitee.

Configuring an Application on Gravitee

The last thing to do before using our API through Gravitee is to configure an Application and subscribe to the API plan we’ve created.

Head to the application section and create an Application with a meaningful name. In our case we’ll create two applications, matching the ones we’ve created in Auth0 earlier.

Create an application

The most important configuration is on the second tab, the client_id. You have to put in the same client_id as in Auth0 ( tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace). This will later on allow Gravitee to match that client_id with a claim found in the JWT token to authorize the API calls.

Configure the client_id with the same value as in Auth0

Then subscribe to the JWT plan you’ve created on the API earlier :

Validate the form to create the application, then head to the API subscriptions and approve the subscription :

That’s it, the Gravitee Application configuration is over. We can also do the same for the other application : gravitee-spa-test.

Now we can try some API calls.

Calling the API through Gravitee

With our client_id and client_secret at hand for the gravitee-spa-test application, we’ll request a token from Auth0 using the client_credentials grant :

curl --location --request POST 'https://your-auth0-tenant.eu.auth0.com/oauth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
"client_id": "tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace",
"client_secret": "your_client_secret",
"grant_type": "client_credentials",
"audience": "your_api_audience"
}'

To which Auth0 responds with a new access token :

{
"access_token": "eyJhb...",
"scope": "your_api_scope_1 your_api_scope_2",
"expires_in": 86400,
"token_type": "Bearer"
}

Using jwt.io you can easily decode the JWT token :

{
"iss": "https://your-auth0-tenant.eu.auth0.com/",
"sub": "tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace@clients",
"aud": "your_api_audience",
"iat": 1617960356,
"exp": 1618046756,
"azp": "tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace",
"scope": "your_api_scope_1 your_api_scope_2",
"gty": "client-credentials"
}

Especially we can verify that the azp claim that stands in the specification for “Authorized party — the party to which the ID Token was issued” is properly set to our client_id tLQa3lQRdjSXj9B6WCBQVRRXdKeNOace.

If we had used the more involved authorization grant type with user interaction authorization_code from the SPA application gravitee-spa-test, we would see in the azp claim the client_id of that other application. We won’t go into more details here because the end-result which is the access token will work exactly the same as with the M2M application.

Finally we can send a request to our gravitee instance using that JWT as an Authorization bearer token.

curl --location --request GET 'https://your-gravitee-hostname/your/api/path' \
--header 'Authorization: Bearer eyJhb.......'

And we get the expected 200 OK of our backend server.

If for some reason you are stuck with 401, 403, 404 errors, here are a couple things you can check to find the cause of the issue :

  • Verify that your API is started in Gravitee which means it can be reached through the gateway
  • Verify that the URL you have configured in the API endpoint panel actually leads to where your API is deployed. If you have an orange bar at the top, click on the button to deploy the API.
  • Verify that your API Plan is Published and not yet in Staging or other state
  • Verify that your application has subscribed to one of the API’s Plan
  • Verify that the token you are using is still valid according to its iat and exp claims (which are unix timestamps)
  • Verify that the azp claim match the value you typed in the client_id of your application in Gravitee
  • Finally you can enable API logging to introspect the HTTP requests headers and bodies to help you investigate what went wrong

This is it, congratualtions ! Your API is now secured with Auth0 JWT tokens and reverse proxied by Gravitee which will allow you to use all its features once you’re familiar with it.

As an additional advice, we’ll finally see how to force API clients to pass through Gravitee for all their API requests.

Last mile security : How to protect the API server

One issue remains though. Nothing prevents malicious API clients to get around the Gravitee Gateway and send an HTTP request directly to your API server if that one is still exposed publicly to the internet.

If your network configuration only exposes Gravitee to the internet and Gravitee talks to your API servers on an internal network you are fine. But depending on your deployment setup that may not be the case and you may still have to expose your API server publicly on the internet.

In that case, the configuration I recommend because it’s easy to setup is Mutual TLS.

Usually, when browsing the internet, your browser consumes TLS certificates to authenticate the server it wishes to communicates with. In that situation only the server is authenticated, the browser remains an anonymous client (regarding the TLS protocol).

But TLS can also be used in both ways to authenticate the client too. In that case, the browser (or any other HTTP client) has to provide a certificate that the server will consume to authenticate that client.

This is exactly what we need, we will instruct our backend API server (or any network component you may have in front of it like a reverse proxy) to authenticate the client. The client in that case is the Gravitee Gateway which relays calls to the API server.

First, generate a private key and a certificate for a custom Certificate Authority :

openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 356 -nodes -subj '/CN=YourCompany'

Then, generate a private key, a certificate signing request and a certificate for your API server :

openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes -subj '/CN=your-api-server-domain.com'openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

Finally, generate a private key, a certificate signing request and a certificate for your Gravitee Gateway client :

openssl req -new -newkey rsa:4096 -keyout client.key -out client.csr -nodes -subj '/CN=Gravitee'openssl x509 -req -sha256 -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -out client.crt

Configure your API server to enable TLS client authentication. Here it depends entirely on your setup. In my case I was using a nginx server deployed as an Ingress Controller under kubernetes on GCP (see the documentation) :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
# Enable client certificate authentication
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
# Create the secret containing the trusted ca certificates
nginx.ingress.kubernetes.io/auth-tls-secret: "default/test-gravitee-ca-secret"
# Specify the verification depth in the client certificates chain
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
# Specify an error page to be redirected to verification errors
nginx.ingress.kubernetes.io/auth-tls-error-page: "http://example.com/"
# Specify if certificates are passed to upstream server
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
generation: 1
labels:
app.kubernetes.io/instance: test-gravitee-auth-api
app.kubernetes.io/name: test-gravitee-auth-api
name: test-gravitee-auth-api
namespace: default
spec:
rules:
- host: your-api-server-domain.com
http:
paths:
- backend:
serviceName: auth-api
servicePort: 8080
path: /api/test-gravitee-auth
tls:
- hosts:
- your-api-server-domain.com
secretName: test-gravitee-tls-secret

Finally on the client side (Gravitee), configure your API endpoint :

Open the API endpoint configuration

In the Configuration section, fill in the server certificate, client private key and client certificate as generated earlier :

That’s it, now your API server will reject any connection where the client doesn’t authenticate with a valid TLS certificate. In that case only Gravitee will have a valid one so that forces your API client to use the Gateway provided by Gravitee to use your API.

Conclusion

This article showed you :

  • How to configure Auth0 to work with Gravitee. In that case, we just needed to create an application or select an existing one
  • Create an API in Gravitee protected with a JWT Plan customized with Auth0 public key
  • Create an Application in Gravitee that matches the client_id in Auth0 and subscribe to the API
  • Make some API calls and provide you with some troubleshooting advices
  • Explain one possibility of securing your API server if it is exposed publicly to the internet

Thank you for reading !

--

--