Azure API Management – JWT validation for multiple Azure AD partner registrations

If there is one popular theme regarding API management, it’s gotta be the subject of facilitating technically partner access via Azure AD. This can be as simple as creating a single app registration to designing full governance model with scopes & roles, and chained App Registrations.

As always, if you want to generally know what is Azure API management, or what is Azure Active Directory, then check the following articles:

Architecture

  • The architecture with new app endpoints enables same app registration to provide native and confidential client access with different settings
  • Scopes are defined per each partner registration

Create Centralized API Management Proxy SPN

  • Here we want to create single-tenant app in order to be able to control roles and authorizations within our tenant for B2B accounts. In multi-tenant scenario this is possible too, but requires that you control most of the authorizations in another management plane, or in scopes only
  • For redirect URI we input nothing, because this application will only act as Service Principal ”Front” for the actual app registrations consuming API management
  • We will use the FQDN of API management as displayName and AppID URL, in later calls we will use this as ’Audience /Resource for the partner clients”

Create Scopes and machine account role for the SPN

There is GUI to define scopes, but I have mine ready on template, so I am gonna use the ”old” experience updating via the manifest to create the scopes
oAuth2Permissions updated via the ”old experience” – Update ”Oauth2Permissions and AppRoles blocks”
 $apps = '{
  "oauth2Permissions": [
     {
         "adminConsentDescription":  "Allow the application to access PartnerAPI-Colda on behalf of the signed-in user.",
         "adminConsentDisplayName":  "Access PartnerAPI-Colda",
         "id":  "bc388acd-37a3-4358-8839-c84b4add4b23",
         "isEnabled":  true,
         "type":  "User",
         "userConsentDescription":  "Allow the application to access PartnerAPI-Colda on your behalf.",
         "userConsentDisplayName":  "Access PartnerAPI-Colda",
         "value":  "user_impersonation"
     },
     {
         "adminConsentDescription":  "Allow the application to access PartnerAPI-Colda on behalf of the signed-in user.",
         "adminConsentDisplayName":  "Access PartnerAPI-Colda",
         "id":  "40cfe68d-bef2-4a4b-8944-fc9766ba17d8",
         "isEnabled":  true,
         "type":  "User",
         "userConsentDescription":  "Allow the application to access PartnerAPI-Colda on your behalf.",
         "userConsentDisplayName":  "Access PartnerAPI-Colda",
         "value":  "user_impersonation2"
     },
     {
         "adminConsentDescription":  "Allow the application to access PartnerAPI-Colda on behalf of the signed-in user as reader.",
         "adminConsentDisplayName":  "Access PartnerAPI-Colda -reader",
         "id":  "a1203e4c-54bf-4e92-bb61-73e1e347cf9d",
         "isEnabled":  true,
         "type":  "User",
         "userConsentDescription":  "Allow the application to access PartnerAPI-Colda on your behalf as writer to the API.",
         "userConsentDisplayName":  "Access PartnerAPI-Colda -reader",
         "value":  "Reader"
     },
     {
         "adminConsentDescription":  "Allow the application to access PartnerAPI-Colda on behalf of the signed-in user as Writer.",
         "adminConsentDisplayName":  "Access PartnerAPI-Colda -writer",
         "id":  "b68e3e4f-7f45-497f-a815-6dc6ef46bbc8",
         "isEnabled":  true,
         "type":  "User",
         "userConsentDescription":  "Allow the application to access PartnerAPI-Colda on your behalf as Writer.",
         "userConsentDisplayName":  "Access PartnerAPI-Colda",
         "value":  "Writer"
     },
     {
         "adminConsentDescription":  "Service Account user Scope",
         "adminConsentDisplayName":  "Service Account App permissions",
         "id":  "508b851a-7393-478a-89b0-f83d1dcc825b",
         "isEnabled":  true,
         "type":  "Admin",
         "userConsentDescription":  "Allow the application to access PartnerAPI-Colda on your behalf as Writer.",
         "userConsentDisplayName":  "Access PartnerAPI-Colda",
         "value":  "Service_Accounts"
     }
                          ]
  }' | ConvertFrom-Json

  foreach ( $app in ($apps.oauth2Permissions)) {
  $app.id =([system.guid]::NewGuid()).guid
  }

  $apps |ConvertTo-Json -Depth 3

#Roles


$apps ='{
"appRoles":  [
      {
          "allowedMemberTypes":  [
                                     "application"
                                 ],
          "displayName":  "ReadOnly-ServiceAccount",
          "id":  "03b62322-1913-4c61-86a7-c325ebd5925f",
          "isEnabled":  true,
          "description":  "ReadOnly-ServiceAccount only to read data.",
          "value":  "ReadOnly-ServiceAccount"
      },
      {
          "allowedMemberTypes":  [
                                     "application"
                                 ],
          "displayName":  "ServiceAccounts",
          "id":  "a59b7301-039e-4782-ad1f-fc443cce1786",
          "isEnabled":  true,
          "description":  "Machine 2 Machine -type integrations.",
          "value":  "ServiceAccount"
      }
  ]
    
}' | ConvertFrom-Json 

  foreach ( $app in ($apps.appRoles)) {
  $app.id =([system.guid]::NewGuid()).guid
  }

  $apps |ConvertTo-Json -Depth 3

Create first partner application

  1. The first partner application is for typical use scenario of Client-Credentials Flow and Authorization Code Flow
From API permissions which we just defined on the manifest we can choose, what kind of user permissions (delegated) and application permissions we can set
In this case the partner needs full serviceAccount permissions, which I’ve set require admin consent
Since there is admin permission required I grant the directory access for the App. In the demo this will grant full scopes for the user. Later we can adjust the behavior of consents and pre-consents quite a lot if needed
Optionally, instead of ”allPrincipals consent” you have the option to leave the consent to the end user, if you’ve decided to make some of the scope end user consent -able (Example is the second partner API2)

Configure JWT validation for Partner Application

Each Partner is identified with their unique App ID claim. The control Any is used, to enable proxying of multiple Partner Clients. 401 is thrown if valid and signature checked AppID isnt found in the incoming Bearer Token
  • Note how we don’t need to reference the Proxy SPN at all? That’s because in previous step we’ve configured permissions for the Partner API to consume the Proxy SPN of API Management, and we need to reference the correct audience only

Test the API as a Partner using Code Authorization Flow

  • Response is what you would expect for Echo API’s create with POST-Verb
We are using ADAL binaries to invoke the code authorization flow
  • For PartnerClientID, we use the AppID we created in the previous step
  • We are using the public client redirect-uri to return the token for the user
This is how the Token Maps to JWT validation in Echo API

Test the API as a Partner using Client Credentials Flow

btw… don’t boughter with the client secret 🙂
  • Response is what you would expect for Echo API’s create with POST-Verb

Thats it for now! I will provide more insights, how to use multi-tenant apps, and how transform scopes into correlating back-end calls in the next blog

Vastaa

Täytä tietosi alle tai klikkaa kuvaketta kirjautuaksesi sisään:

WordPress.com-logo

Olet kommentoimassa WordPress.com -tilin nimissä. Log Out /  Muuta )

Google photo

Olet kommentoimassa Google -tilin nimissä. Log Out /  Muuta )

Twitter-kuva

Olet kommentoimassa Twitter -tilin nimissä. Log Out /  Muuta )

Facebook-kuva

Olet kommentoimassa Facebook -tilin nimissä. Log Out /  Muuta )

Muodostetaan yhteyttä palveluun %s