Automating SwaggerHub User Management with Azure AD, Microsoft Graph, and Azure Functions

Written by fkilcommins | Published 2022/01/18
Tech Story Tags: api | automation | user-management | swagger | automating-user-management | swaggerhub | software-development | azure

TLDRThe API industry is accelerating as organizations increase their participation in the digital economy. Most organizations see software delivery as a core competency. The prevalence of microservices and as-a-service value propositions are fueling the shift from monolithic systems to a more distributed and composable architecture. All of this leads to a proliferation of APIs. As API estates increases across the enterprise landscape, so do the number of stakeholders involved in the API lifecycle. This highlights the importance of effective license and user management. Left unmanaged, organizations risk unauthorized access to their API artifacts, and potential cost implications. Learn how to combine change notifications, Azure Functions, and the SwaggerHub User Management API to have automated user management between Azure Active Directory (AD) and SwaggerHub (or your solution of choice). The result is less headache and more efficiency when it comes to taming your API program. via the TL;DR App

If you’re not using SwaggerHub but have other solutions that you need to automate user management from Azure AD, then you can still follow along and just swap out SwaggerHub APIs for your specific solution user management APIs

Key Takeaways

  • Microsoft Graph Change Notifications can be used to get events on created, updated, or deleted resources across Microsoft 365
  • Combine change notifications, Azure Functions, and the SwaggerHub User Management API to have automated user management between Azure Active Directory (AD) and SwaggerHub (or your solution)
  • Easily deploy an automated solution to Azure with ARM templates and GitHub Actions
  • User Management API for SwaggerHub supports several operations to help organizations automate the provisioning and management of users

Intro

The API industry is accelerating as organizations increase their participation in the digital economy. Most organizations see software delivery as a core competency. The prevalence of microservices and as-a-service value propositions are fueling the shift from monolithic systems to a more distributed and composable architecture. All of this leads to a proliferation of APIs.

APIs are the cornerstone for value exchange both inside and outside of the enterprise. API quality and developer experience (DX) is no longer optional. An API First approach must be backed by tools and processes to empower teams to automate standards, governance, and quality throughout the delivery of an API product. This is crucial for meeting customer expectations and accommodating scalability requirements on the producer side.

SwaggerHub is a popular solution for managing API design, collaboration, and development. Until recently, management of API designers, owners, and collaborators had to be performed entirely within the SwaggerHub user interface. Now, with the recent delivery of the User Management API, management can be automated and integrated more holistically with existing identity and access management practices.

As API estates increase across the enterprise landscape, so do the number of stakeholders involved in the API lifecycle. This highlights the importance of effective license and user management. Left unmanaged, organizations risk unauthorized access to their API artifacts, and potential cost implications.

Use Case – SwaggerHub User Management via Azure AD

Many organizations use Azure AD identity and access management for authentication and as a way to control employee access to resources and applications. Many businesses require that application access be centrally provisioned from the Azure AD tenant or via an HR system linked to the identity tenant.

In this article, I demonstrate how to automate SwaggerHub user management based on the employee assignment to Azure AD groups. This eliminates the need to perform user management for API stakeholders directly within SwaggerHub.

Benefits this brings:

  • Onboarded users will automatically get access to SwaggerHub, assuming the appropriate Azure AD group membership is applied
  • Users leaving the company are automatically removed from SwaggerHub once their group memberships are revoked
  • Users changing roles, or moving between departments or teams, will have their SwaggerHub access modified based on the changes in Azure AD group assignments
  • Improved security by reducing the likelihood of unauthorized access to your APIs caused by dual user management (e.g., if a user who left the organization was not removed from SwaggerHub)
  • Improved license management

Solution Component Overview

As we’re dealing with Azure AD, I provide an automation solution that leverages other Microsoft components.

The main components used in the solution are:

  • Microsoft Azure AD – a universal platform to manage and secure identities which provides single sign-on and multi-factor authentication.
  • Microsoft Graph – a gateway to data and intelligence across the Microsoft suite, using REST APIs and client libraries.
  • Azure Functions – Microsoft’s serverless compute service.
  • SwaggerHub - the collaborative design and documentation platform for teams and individuals working with the OpenAPI Specification
  • SwaggerHub User Management API – an administrative API that allows organizations to automate the on-boarding and off-boarding of users

The solution consists of two Azure Functions which interact with the Microsoft Graph API and the SwaggerHub User Management API. The first function, SubscriptionManager, manages subscriptions to Microsoft Graph change notifications.

The second function, SwaggerHubUserManager, receives notifications (or events) from the Microsoft Graph and parses them. When appropriate, it will interact with the SwaggerHub User Management API to create, update, or delete users.

Before we jump into the details of the solution, let’s understand Microsoft Graph Change Notifications.

Microsoft Graph Change Notifications

The Microsoft Graph REST API uses a webhook mechanism to deliver change notifications to a subscribed client. When the resource/entity subscribed for is created, updated, or deleted, Microsoft Graph submits an HTTP POST to the specified client endpoint. The client endpoint listens for these messages and acts on them by executing logic defined based on business requirements.

A client application can get notifications about messages, events, contacts, users, groups, conversations, OneDrive files, alerts, etc. In many cases, it’s beneficial to trigger application behavior based on changes or events, and it removes the need for other implementations like polling.

A client application must have a valid subscription in order to receive a change notification. Subscriptions to change notifications expire, and subscription lifetimes vary. Subscription lifecycles need to be managed to ensure that client applications continue to receive notifications.

Azure Application and AD Group Setup

To deploy or run the solution locally, you need to have an Azure subscription and an Azure AD tenant setup.

To ensure the Azure Functions can interact with the Microsoft Graph API, you need to register an application within your Azure subscription and assign the necessary Microsoft Graph API permission.

Register Azure Application

To register an application within the Azure portal:

  1. Login to the Azure Portal
  2. Search for App Registrations using the global search bar (or via Menu > All services > App Registrations)
  3. Click New Registration
  4. Set a Name for your application (e.g., MSGraph Notifications for SwaggerHub)
  5. Depending on your multi-tenancy requirements, choose the supported account types. If unsure, choose the Accounts in this organizational directory only option
  6. Set a Redirect URI (e.g., http://localhost)
  7. Click Register

Be sure to record your Application (client) ID and Directory (tenant) ID from the application overview section as you'll need them later.

Grant Permission on Microsoft Graph

You want this application to receive change notifications from the Microsoft Graph when the Groups resource changes, and to be able to retrieve information on the users added/removed from a group. To do this, the following permissions are required:

  • Group.Read.All - Read all groups
  • User.Read.All - Read all users' full profiles

To add the permissions to your registered app:

  1. Navigate to the API permissions menu option beneath the Manage section of the left-hand navigation menu
  2. Click Add a permission
  3. Choose Microsoft Graph from the Microsoft APIs > Commonly used Microsoft APIs section
  4. Choose Application permissions as your application will be invoking these APIs using the client_credentials flow and not on behalf of a user
  5. Under Group permissions, select Group.Read.All
  6. Under User permissions, select User.Read.All
  7. Click Add permissions
  8. Click Grant admin consent for and click Yes on the Grant admin consent confirmation dialog

Azure AD Group Structures

Granting or revoking certain group memberships within Azure AD should add, update, or remove users from SwaggerHub. For this to work, you need to configure the representative groups within your Azure AD tenant.

The AD Group creation strategy will change, depending on how many SwaggerHub organizations you have. This solution supports both single and multi-organization setups.

Single SwaggerHub Organization

If you have a single SwaggerHub organization, the recommended group setup is to create an AD Group per SwaggerHub role.

SwaggerHub Organization

SwaggerHub Role

AD Group Name (example only - follow your own naming conventions)

Description

Org 1

CONSUMER

ADG-SWAGGERHUB-CONSUMERS

AD Group for consumers that will get access to SwaggerHub

Org 1

DESIGNERS

ADG-SWAGGERHUB-DESIGNERS

AD Group for designers that will get access to SwaggerHub

Org 1

OWNERS

ADG-SWAGGERHUB-OWNERS

AD Group for owners that will get access to SwaggerHub

Multiple SwaggerHub Organizations

If you have multiple organizations within SwaggerHub, you have a few options for structuring your organizational groups. You can linearly extend the examples above and be very clear about setting up your groups per organizations - for instance, including the SwaggerHub organization name in the Azure AD Group name (e.g., ADG-SwaggerHub-<ORG NAME>-<ROLE NAME>).

Assuming you have two organizations, resulting in six AD groups:

SwaggerHub Organization

SwaggerHub Role

AD Group Name (example only - follow your own naming conventions)

Description

Org 1

CONSUMER

ADG-SWAGGERHUB-ORG1-CONSUMERS

AD Group for consumers that will get access to SwaggerHub

Org 1

DESIGNERS

ADG-SWAGGERHUB-ORG1-DESIGNERS

AD Group for designers that will get access to SwaggerHub

Org 1

OWNERS

ADG-SWAGGERHUB-ORG1-OWNERS

AD Group for owners that will get access to SwaggerHub

Org 2

CONSUMER

ADG-SWAGGERHUB-ORG2-CONSUMERS

AD Group for consumers that will get access to SwaggerHub

Org 2

DESIGNERS

ADG-SWAGGERHUB-ORG2-DESIGNERS

AD Group for designers that will get access to SwaggerHub

Org 2

OWNERS

ADG-SWAGGERHUB-ORG2-OWNERS

AD Group for owners that will get access to SwaggerHub

Alternatively, you can define your own group structures based on your organizational domains, and map them to the existing SwaggerHub Orgs which can be less verbose and more intuitive. Other configuration examples for multiple organization setups can be found in the GitHub repo. Your Azure AD administrator can set up the appropriate AD groups based on your chosen strategy.

Solution Walkthrough

The full source code for the solution (here) can be deployed to your Azure subscription using the deployment options described below.

The .NET 5 solution, predominately consists of two Azure functions with some Models and supporting Services for interacting with the Microsoft Graph API and the SwaggerHub User Management API.

SubscriptionManager Azure Function

This is a TimerTrigger function which runs daily at noon based on the configured CRON expression ‘0 0 12 * * *’. It manages the change notification subscription lifecycle with the Microsoft Graph. It creates a new subscription if one does not exist; otherwise, it will renew an existing subscription if it’s due to expire within seven days, which means our SwaggerHubUserManager  function won’t miss any change notifications.

Interaction with the Microsoft Graph API is through the GraphService class, which is a trimmed version of MS Graph SDK capabilities for the needs of the solution. It initializes a GraphServiceClient instance and takes care of obtaining the OAuth 2.0 token which is required to invoke the MS Graph endpoints.

As visible in Figure 4 above, the service offers methods to manage the lifecycle of the subscription, and the SubscriptionManager function interacts with the service methods accordingly (see snippet below):

try
{
	// Get subscriptions
	var longestToLiveSubscription = await _graphService.GetSubscriptionWithLongestTimeToLive();
	
 
	// if no subscription then create subscription
	if(longestToLiveSubscription == null)
	{
	  logger.LogInformation($"No subscription found >> creating new subscription...");
	  await _graphService.CreateChangeNotificationSubscription();
	}
	else
	{
	  // if subscription will expire in less than a week renew the subscription
	  if(DateTime.UtcNow.AddDays(7) > longestToLiveSubscription.ExpirationDateTime)
	  {
	    logger.LogInformation($"Subscription {longestToLiveSubscription.Id} will expire within 7 days >> renewing subscription...");
	    await _graphService.RenewSubscription(longestToLiveSubscription);
	  }
	  else
	  {
	    logger.LogInformation($"Subscription {longestToLiveSubscription.Id} will expire at [{longestToLiveSubscription.ExpirationDateTime}] >> no need to renew at this time...");
	  }
	}               
}
catch(Exception ex)
{
	logger.LogError($"Exception thrown: {ex.Message}");
} 

Alternatively, you could perform similar subscription management manually by invoking the MS Graph APIs and repeating this activity prior to subscription expiry.

Get Subscriptions

curl --location --request GET 'https://graph.microsoft.com/v1.0/subscriptions/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer PUT_YOUR_TOKEN_HERE'

Create Subscriptions

curl --location --request GET 'https://graph.microsoft.com/v1.0/subscriptions/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer PUT_YOUR_TOKEN_HERE' \
--data-raw '{
  "changeType": "updated,deleted",
  "notificationUrl": "URL to endpoint that will receive events",
  "resource": "groups",
  "expirationDateTime": "2021-12-30T22:19:41.561Z",
  "clientState": "*someSecretKnownByPubandSub*"
}' 

Update Subscriptions

curl --location -g --request GET 'https://graph.microsoft.com/v1.0/subscriptions/{id}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer PUT_YOUR_TOKEN_HERE' \
--data-raw '{
  "expirationDateTime": "2021-12-01T22:19:41.561Z"
}' 

SwaggerHubUserManager Azure Function

This HTTPTrigger function is the heart of the solution and is called by the Microsoft Graph webhook anytime there is a change to group memberships within the Azure AD tenant. The function parses the received change notification and determines if it’s required to process the change notification. To decide, it leverages the tenant specific configuration that’s set up in the GroupConfiguration.json file. If the received change notification relates to a group referenced in the configuration file, it goes back to the Microsoft Graph to retrieve specific details on the users which were added to or removed from the AD group membership. Then it calls the SwaggerHub User Management API to ensure that changes are automatically reflected in the appropriate SwaggerHub organizations.

Interaction with the SwaggerHub User Management API is through the SwaggerHubUserManagementService class. This service prepares the requests to, and handles the responses from, the SwaggerHubRepository class. This class is a wrapper around an HTTPClient exposing generic GET, POST, PATCH, and DELETE methods and sets some base SwaggerHub settings like apiKey, base URL, API path, and API version. This repository class simplifies the API calls for the upper service layer, so that it doesn’t need to negotiate an HTTP request.

It’s worth also noting that any endpoint registered to receive change notification webhook calls from the Microsoft Graph must be able to acknowledge the subscription registration request within 200ms. Additionally, it MUST return the validation code which was issued in the verification request.

The following snippet demonstrates how the SwaggerHubUserManagerFunction acknowledges the verification request based on the existence of a validationToken query parameter. The validationToken received must be returned in plain text.

// basic validation of request
var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);
string token = query["validationToken"];
 
if(!string.IsNullOrEmpty(token))
{
	//acknowledge back with validation token
	logger.LogInformation($"Validation Token: {token}");
	var ackResponse = req.CreateResponse(HttpStatusCode.OK);
	ackResponse.Headers.Add("Content-Type", "text/plain; charset=utf-8");
	ackResponse.WriteString(token);
	return ackResponse;
} 

The solution can support single SwaggerHub organization configurations, as well as complex multi-organizational setups as discussed in the “Azure AD Group Structures” section above. Once you have finalized the appropriate setup, you need to populate the GroupConfiguration.json file to reflect the setup. This acts as a link between your Azure AD tenant and your SwaggerHub organization, and it’s leveraged by the Azure function to:

  • determine the list of Azure AD groups that it reacts to when a notification is received from MS Graph
  • determine what SwaggerHub organization and role combination to use when creating or modifying a user in SwaggerHub

A sample configuration file representing a three group single organization setup might look like:

{
	"GroupConfiguration": {
		"activeDirectoryGroups": [
			{
				"objectId": "bc9ae6d5-1ccf-45dd-b56f-9ca020348802",
				"name": "ADG-SWAGGERHUB-DESIGNERS",
				"swaggerHubRole": "DESIGNER",
				"organizations": [
					{
						"name": "frank-kilcommins"
					}
				]
			},
			{
				"objectId": "afd8b023-331d-4fc4-84f7-f5a4654cbbbd",
				"name": "ADG-SWAGGERHUB-CONSUMERS",
				"swaggerHubRole": "CONSUMER",
				"organizations": [
					{
						"name": "frank-kilcommins"
					}
				]
			},
			{
				"objectId": "126f28b7-a2b6-4a87-af23-edb61af80317",
				"name": "ADG-SWAGGERHUB-OWNERS",
				"swaggerHubRole": "CONSUMER",
				"organizations": [
					{
						"name": "frank-kilcommins"
					}
				]
			}
		]
	}
}

The main object configured is the activeDirectoryGroup object which stores the mapping details, allowing the solution to manage SwaggerHub organization(s) based on a particular Azure AD Group.

Property

Property (nested)

Required

Description

objectId

yes

The objectId of the AD Group as autogenerated by the tenant

name

no

The name of the AD Group as set within the tenant

swaggerHubRole

yes

The role that will be set in SwaggerHub for users receiving membership to the AD Group referenced by the objectId. Allowed options are CONSUMER, DESIGNER or OWNER

organizations

yes

The SwaggerHub organizations mapped or linked to the AD Group referenced by the objectId

name

yes

The name of the organization as created within SwaggerHub

The solution comes with a built-in JSONSchema to validate the GroupConfiguration.json and the configuration is validated against the schema during startup.

Deployment Options

The solution is almost turnkey and gives you two main options for deployment.

Option 1

Leverage the “Deploy to Azure” button to deploy the required Azure infrastructure to your Azure subscription.

Note: You still need to publish the code manually to your resources. Follow the Setting Up Locally section in the GitHub repo and deploy your code to your Azure environment.

Option 2

Fork the source code to your own repo and create the following GitHub action:

on: [workflow_dispatch]  # change the triggering mechanism to suit your needs (manual run by default)

name: AzureAD-SwaggerHub-UserManagement-Setup-and-Deploy

env:
  AZURE_FUNCTIONAPP_PACKAGE_PATH: 'src'   # set this to the path to your web app project, defaults to the repository root
  DOTNET_VERSION: '5.0.402'               # set this to the dotnet version to use

jobs:

  # use ARM templates to set up the Azure Infra
  deploy-infrastructure:
    runs-on: ubuntu-latest

    # set outputs needed by subsequent jobs
    outputs:
      azFunctionAppName: ${{ steps.armdeploy.outputs.functionAppName }}
    
    steps:

    # check out code
    - uses: actions/checkout@main

    # login to Azure
    - uses: azure/login@v1
      with:
        creds: ${{ secrets.AzureAD_SwaggerHub_CREDENTIALS }}

    # deploy ARM template to setup azure resources (group & sub defined in credentials)
    - name: Run ARM deploy
      id: armdeploy
      uses: azure/arm-deploy@v1
      with:
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
        resourceGroupName: ${{ secrets.AZURE_RG }}
        template: ./azuredeploy.json
        parameters: ./azuredeploy.parameters.json
  

  # build and deploy our Azure functions for SwaggerHub + Azure AD user mgmt
  build-and-deploy:
    needs: [deploy-infrastructure]
    runs-on: windows-latest
    environment: prd
    steps:
    # check out code
    - name: 'Checkout code'
      uses: actions/checkout@main

    # login to Azure
    - uses: azure/login@v1
      with:
        creds: ${{ secrets.AzureAD_SwaggerHub_CREDENTIALS }}
        enable-AzPSSession: true      

    # get publish profile
    - name: Get publish profile
      id: fncapp
      uses: azure/powershell@v1
      with:
        inlineScript: |
          $profile = ""
          $profile = Get-AzWebAppPublishingProfile -ResourceGroupName ${{ secrets.AZURE_RG }} -Name ${{ needs.deploy-infrastructure.outputs.azFunctionAppName }}
          $profile = $profile.Replace("`r", "").Replace("`n", "")
          Write-Output "::set-output name=profile::$profile"
        azPSVersion: "latest"

    # setup donet environments
    - name: Setup DotNet Environments
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: |
          3.1.x
          ${{ env.DOTNET_VERSION }}

    # build project
    - name: 'Resolve dependencies and build'
      shell: pwsh
      run: |
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
        dotnet restore
        dotnet build --configuration Release --output ./output
        popd

    # publish azure function
    - name: 'Run Azure Functions Action'
      uses: Azure/functions-action@v1
      id: fa
      with:
        app-name: ${{ needs.deploy-infrastructure.outputs.azFunctionAppName }}
        package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output'
        publish-profile: ${{ steps.fncapp.outputs.profile }}

The GitHub action above, needs the following secrets configured:

  • Azure_SUBSCRIPTION - your Azure subscription ID
  • Azure_RG - the name of the Azure Resource Group that you want to deploy this solution into
  • AZUREAD_SWAGGERHUB_CREDENTIALS - the credentials for a Service Principal with contributor access for the resource group. See how to get the Service Principal credentials.

Both options above leverage the Azure Resource Manager (ARM) Template to deploy the following resources to a named Azure Resource Group:

  • Azure App Service Plan
  • Azure Function App
  • Azure Storage Account

Alternatively, you can create the resources manually using the Azure portal or Azure CLI, and deploy the code using whatever process works well for your organization.

Conclusion

Organizations around the world are adopting APIs to enable digitalization and improve connectivity, but they face increasing challenges in managing the API sprawl. One such challenge is how to manage the ways in which teams and individuals access their API tooling, especially if each tool mandates user management directly within its own management UI.

Thankfully, the User Management API for SwaggerHub has many ways to help organizations automate the provisioning and management of users, maintain secure access to their proprietary APIs, and maximize their licenses allocations.

The API is available for organizations on Enterprise and Enterprise Trial plans, as well as for SwaggerHub On-Premise customers.

By applying the solutions explained in this article, you can help your organization take advantage of the new user-management APIs offered by SwaggerHub. These work in seamless combination with your Azure AD Identity and Access Management suite. The result is less headache and more efficiency when it comes to taming your API program.

Even if you’re not using SwaggerHub, then I hope you can benefit from some of the techniques laid out above to leverage Azure AD, Change Notifications and Azure Functions in combination with your own application APIs for improved user management automation.


Written by fkilcommins | API Technical Evangelist SmartBear. 15+ years tech industry experience. Software engineering, architecture & ❤️ing APIs
Published by HackerNoon on 2022/01/18