Use MITREid Connect for OAuth2 Authorization in API Management
Published Oct 05 2020 03:08 PM 5,729 Views
Microsoft

Use MITREid Connect for OAuth2 Authorization in API Management

 

By (alphabetically): Akinlolu Akindele, Dan Balma, Maarten Van De Bospoort, Erin Corson, Nick Drouin, Heba Elayoty, Andrei Ermilov, David Giard, Michael Green, Alfredo Chavez Hernandez, Hao Luo, Maggie Marxen, Siva Mullapudi, Nsikan Udoyen, William Zhang

 

Introduction

 

Using an API gateway in front of REST APIs is a common design pattern which allows us to offload the cross-cutting capabilities such as OAuth2 authorization to the gateway instead of letting security code get scattered in application code. Azure API Management (APIM) is such an API gateway service. For the common case that the OAuth2 server in APIM is Azure AD (AAD), it has been well documented. In this document we will cover the case that the OAuth2 server is MITREid Connect instead of AAD.

 

The following diagram illustrates such a scenario: the backend REST API runs on an Azure Kubernetes Service (AKS) cluster. While the API Management endpoint is secured by OAuth2 authorization, the AKS endpoint is secured by mutual TLS (mTLS) between API Management and AKS. The setup of mTLS between AKS and API Management is covered by a sister document.
 
security_oauth2.drawio.png

 

MITREid Connect is an open source Identity Provider, popular in Java community. MITREid Connect is compliant to OpenID Connect and OAuth 2.0 protocol. This document is based on MITREid Connect v 1.3.3.

 

Prerequisites

 

  1. An Azure subscription
  2. A MITREid Connect instance. This document focuses on how to enable OAuth2 over API Management and MITREid Connect, instead of on how to set up MITREid Connect.
  3. A REST API app for test. The REST API could be deployed to AKS, App Service, or other. Or use this sample REST API with Swagger http://conferenceapi.azurewebsites.net/?format=json.
  4. Familiar with Azure API Management
  5. Basic understanding of OAuth2. OAuth2 involves a few specs (OAuth2, OIDC, PKCE and JOSE). A list of good references on OAuth2 and related specs can be found here.

NOTE: This article does not cover the security between APIM and the REST API which is a separate topic. If a REST API is deployed to AKS, there are a few options which have been documented in Use Azure API Management with microservices deployed in Azure Kubernetes Service.

If API Management Premium is used, we can leverage VNET so that the security between API Management and AKS is simpler. Otherwise, we need to secure the REST calls from API Management to AKS thru technique such as mutual TLS (mTLS) (note that it is mutual TLS, not just TLS). Please see this sister document

 

Preparations in MITREid Connect

 

In order to prepare for OAuth2 setup in API Management, we need to perform the following steps in MITREid Connect:

  1. Register a server application registration;
  2. Define its scopes;
  3. Register a client application;
  4. Specify its permissions: either Delegated Permissions or Application Permissions;
  5. Create a client secret.

The concepts and the steps for the above OAuth2 steps between MITREid Connect and Azure AD are not fundamentally different. You may get detailed steps from Protect an API by using OAuth 2.0 with Azure Active Directory and API Management

 

The following parameters from MITREid Connect must be prepared and available for our setup in API Management.

 

Parameter Typical Format
Client ID A string. Unlike Azure AD, a client ID in MITREid Connect does not have to be a GUID. But it needs to be unique within its tenant.
Client secret Hexadecimal representation of a cryptographically secure pseudorandom number
OpenID config endpoint https://[host]/.well-known/openid-configuration
Authorization endpoint https://[host]/authorize/
Token endpoint https://[host]/token/
Audience A string
Issuer https://[host]/
Default Scopes Optional, and it could have multiple values

 

Create an OAuth2 Server on MITREid Connect

 

  1. In Azure portal left menu column, click "OAuth 2.0"
  2. Click "Add" button on the right pane
  3. You need the following parameters to create the OAuth 2 server on MITREid Connect:
    • Client ID
    • Client secret
    • Authorization endpoint
    • Token endpoint

For Authorization grant types, you may choose to checkmark the following:

  1. Authorization code
  2. Client credentials

For Default scope, you can either leave it blank or enter any valid scope as defined in MITREid Connect client registration.

You can keep Resource owner username and Resource owner password as blank.

For additional details on creating OAuth2 server in API Management, please see this document.

 

Set up a REST API App in API Management

 

  1. Create an API Management service by following this document.
  2. Import a REST API by following this document.
  3. Make sure to disable subscription-key authorization which is enabled by default.
    1. Click on "APIs" menu in the left menu column
    2. Select the API you are working with in the mid-column
    3. In the right pane, select Settings tab at the top
    4. Uncheck Subscription required under Subscription section. This ensures that subscription-key authorization is not used since we intend to use OAuth2 authorization.

 

Customize API Inbound Policy

 

  <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
        <openid-config url="[openid-config endpoint]" />
        <required-claims>
              <claim name="aud">
                 <value>[audience]</value>
              </claim>
              <claim name="iss">
              <value>[issuer]<value>
              </claim>
              <claim name="scope">
                 <value>[scope 1]</value>
                 <value>[scope 2]</value>
              </claim>
        </required-claims>
  </validate-jwt>

The openid-config endpoint is the OpenID Config URL from MITREid Connect. It is critical to make sure you have the correct URL since this is the URL thru which API Management acquires JWKS (JSON Web Key Set) for JWT validation.

The values for audience and *issuer" are from MITREid Connect and can be found from its token sample.

For *scope" claim, you can have 0, 1 or multiple scope values. Notice that if you have mutliple scope values, it means AND (all required) instead of OR (one of scope values is enough).

 

Troubleshooting Guide

 

In OAuth2 authorization, it is typical to have multiple parts involved in the authorization flow:

 

Component Purpose
Client application The client application can be either an app for end users or a service/server process depending on the secured REST API. In case of a client app for end users, it can be either a private client which can hide client_secret or a public client which cannot. Depending on use case, different OAuth2 authorization flows can be used. If the client is a service, a Client Credentials Flow is used. For a public end user client, Authorization Code Flow with PKCE is used such as in the OAuth2 Test Tool.
Identity Provider Users get authenticated and are issued authorization code which can be used to acquire access_token.
Token issuer The MITREid Connect component which issues access_tokens and refresh_tokens
Token introspector The API Management component which inspects and validates JWT tokens based on policy settings, such as claims, issuer and audience
OpenID config endpoint An endpoint provided by MITREid Connect from which API Management acquires public keys for token introspection. This is based on OpenID Discovery spec. API Management is never configured to hold a static public key from MITREid Connect.
Registered client app A client app pointer in MITREid Connect which defines the scopes granted by either admin or users, as well as its client ID and client_secret
Registered server app A server app pointer in MITREid Connect which defines an abstraction of REST API and defines the scopes

 

Unauthorized (401) Issue

 

The most common error in OAuth2 authorization is Unauthorized (401). This could be caused by any of the following:

  1. Incorrect openid-config endpoint in API inbound policy
  2. Incorrect *iss" value in API inbound policy
  3. Incorrect "aud" value in API inbound policy
  4. Incorrect "scope" values in API inbound policy. If you can capture a sample JWT token, you can parse it via a tool like http://aka.ms/jwt to see its claims values. These values are determined by registered server application in MITREid Connect and the API permission configurations for the registered client application.
  5. Incorrect Security settings in the API. Check the *Settings" tab of the API and make sure that *User authorization" is OAuth 2.0 and the right OAuth 2.0 server is selected in the OAuth 2.0 server dropdown.

 

Subscription-key Issue

 

By default, when an API is installed into API Management service, its subscription-key authorization is enabled. Make sure it is disabled since we use OAuth2 instead of subscription-key. You can find its settings under Settings tab in the API.

 

CORS Issue

 

If your test client is a web or SPA client and you use javascript to make API calls with OAuth2 authorization, you will likely face CORS issue since the web app or SPA is from different domain as API Management. By default, an API in API Management instance does not support CORS preflight and you need to enable it in inbound policy.

Inbound section:

  <cors allow-credentials="true">
        <allowed-origins>
            <origin>http://localhost:3000</origin>
        </allowed-origins>
        <allowed-methods preflight-result-max-age="300">
            <method>GET</method>
            <method>POST</method>
            <method>PATCH</method>
            <method>DELETE</method>
            <method>PUT</method>
        </allowed-methods>
  </cors>

 

Outbound section:

  <outbound>
     <base />
     <set-header name="Access-Control-Allow-Origin" exists-action="override">
           <value>@(context.Request.Headers.GetValueOrDefault("Origin",""))</value>
     </set-header>
     <set-header name="Access-Control-Allow-Credentials" exists-action="override">
           <value>true</value>
     </set-header>
  </outbound>

 

Custom header issue

 

Another potential issue is: for a production REST API, it has custom headers such as transaction or correlation ID for logging and troubleshooting. Such correlation ID may be scoped to the enterprise or the service itself.

However, there is a restriction to access response headers when you are using javascript Fetch API over CORS. Due to this restriction, by default you can access only following standard headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

More info on this restriction can be found here.

In order to allow such client to get access to custom headers (such as correlation ID), we need to add the following section in our inbound/cors section:

 

  <allowed-headers>
        <header>content-type</header>
        <header>accept</header>
        <header>authorization</header>
        <header>x-correlation-id</header>
        <header>x-my-request-id</header>
  </allowed-headers>
  <expose-headers>
        <header>x-correlation-id</header>
        <header>x-my-request-id</header>
  </expose-headers>

 

Terraform for Deployment

 

Needless to say, it is desirable to have the creation and configuration automated. We could use Terraform for this purpose. Detailed document on Terraform Azuure API Management Resources can be found here.

The Terraform (.tf file) should cover the following tasks:

  1. Create a resource group
  2. Create an API Management service
  3. Create a product
  4. Add a REST API into the product
  5. Disable subscriptin key authorization in the REST API
  6. Import a prepared inbound policy (XML) into the REST API
  7. Add OAuth2 Server based on MITREid Connect
  8. Configure REST API settings

In addition to the variables defined in variables.tf file, the Terraform also requires a XML policy file as input. In the policy file, the following parameters (XML node/attribute values) are required.

 

XML node Node attribute Definition
openid-config url The OpenID Config endpoint URL for public keys used in token introspection
claim name (aud) Audience defined in the registered server app in MITREid Connect
claim name (iss) The issuer of JWT tokens
claim name (scope) The list of required scopes. All of the listed scopes listed must be present in a token before it can be validated.
allowed-origins   List of allowed origins for CORS preflights

 

The APIM inbound policy with these parameters is critical in ensuring the following to work:

  • Token introspection
  • CORS policy
  • Custom header accessibility

 

Version history
Last update:
‎Dec 08 2020 10:06 AM
Updated by: