Protecting Azure API Management with Firebase Authentication

  • Azure
  • Firebase
  • API Management

Azure API Management is Azure's solution for building API gateways. One of it's many features it's allowing us to gather all our backend endpoints and expose them behind a single entry point, taking care of the routing behind the scenes and letting us define an access policy to be applied to all of them.

In this post I'll explain how to protect an API with Google's Firebase Authentication service, defining a policy to reject all requests without a valid JWT.

If you're are just looking for the XML policy, you can skip the explanation and jump right to it.

The validate-jwt policy

API Management behavior can be customized using policies. A policy is an XML document that describes what the service needs to do when receiving a request - inbound policies, forwarding the request to the backend - backend policies, or returning the response to the sender - outbound policies. Additionally, we can define on error policies to be applied when an error is encountered while processing a request.

Since we need to validate a JWT before forwarding the request to our backends, the type of policies we are looking for are the inbound policies. Fortunately for us, there is already a built-in policy to validate an arbitrary JWT named validate-jwt. We'll need the following values to configure it:

  • Firebase project ID
  • Header name
    • Name of the header containing the JWT. We'll use the standard Authorization header on this post.
  • OpenID Connect Discovery endpoint
    • An endpoint that provides the OpenID server metadata, like the public keys necessary to validate the integrity of the token. For Firebase, we need to use https://securetoken.google.com/{projectId}/.well-known/openid-configuration. Find more details about these endpoints on the OpenID Connect Discovery specification.
  • Token audience
    • The audience intended for the token. We need to use the Firebase project ID according to Firebase docs.
  • Token issuer
    • The issuer of the token. We need to use https://securetoken.google.com/{projectId} according to Firebase docs.

Location of the Firebase project ID

Image of the location of the Firebase project ID

Final policy

Once we finished gathering the information, our policy will end like the following.

<validate-jwt header-name="Authorization">
  <openid-config url="https://securetoken.google.com/{projectId}/.well-known/openid-configuration" />
  <audiences>
    <audience>{projectId}</audience>
  </audiences>
  <issuers>
    <issuer>https://securetoken.google.com/{projectId}</issuer>
  </issuers>
</validate-jwt>

Now let's continue by configuring our API Management service to use it.

Configuring the policy on API Management

The next step is to configure the policy on our API Management instance. For this post, we'll be testing the policy on an API that's connected to an Azure Function, but the process it's the same no matter what kind of backends are you using.

  1. Log in to the Azure Portal and navigate to your API Management instances.

  2. Navigate to APIs, and then select the API you want to protect.

  3. Click on All operations, and select the code view for Inbound processing.

    Image of the location of the code view button for Inbound processing
  4. Inside the XML editor, copy & paste the policy within the <inbound> tags. Don't forget to replace the {projectId} placeholder for your actual ID 😊 (azure-api-management-auth in my case). Save the changes once you finished.

    Image of the final result of the policy inside API Management

Testing the policy

Now to test our work, we need a Firebase JWT. To do so I'll activate e-mail/password authentication on my Firebase project, and then a HTTP request with some test credentials I've created:

POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={your-project-api-key}

With body:

{
  "email": "john.doe@test.com",
  "password": "12345678",
  "returnSecureToken": "true"
}

This will return a response like the following, from which we need the idToken property:

{
  "kind": "identitytoolkit#VerifyPasswordResponse",
  "localId": "some-id",
  "email": "john.doe@test.com",
  "displayName": "",
  "idToken": "eyJhbG...",
  "registered": true,
  "refreshToken": "some-token",
  "expiresIn": "3600"
}

Now let's do a GET request to our new protected API, but without an authentication token: Image of the unauthenticated request returning a 401 HTTP code

As you can see, the request failed with a 401 Unauthorized HTTP code as expected. Now let's try adding the header Authorization with Bearer eyJhbG... as value: Image of the authenticated request returning a 200 HTTP code

Great! This time the request succeded and returned a 200 OK with the response of the underlying function. This means that our API is now correctly protected behind Firebase tokens.

Finale

Hope you find this useful, and thank you for reading. Feel free to leave your thoughts on the comments below 👇.