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
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.
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:
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:
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.
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.
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.
To register an application within the Azure portal:
http://localhost
)Be sure to record your Application (client) ID and Directory (tenant) ID from the application overview section as you'll need them later.
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 groupsUser.Read.All
- Read all users' full profiles
To add the permissions to your registered app:
Group.Read.All
User.Read.All
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.
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 |
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.
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.
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"
}'
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:
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.
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 IDAzure_RG
- the name of the Azure Resource Group that you want to deploy this solution intoAZUREAD_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:
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.
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.