Authentication for Micro-services using Azure AD
In today's interconnected world, securing access to resources is a fundamental requirement for any application. OAuth is a popular open standard for authorization that enables applications to securely access user resources. However, implementing OAuth can be complex, especially in microservices architectures where multiple services need to communicate with each other securely. This is where Azure AD comes in - a cloud-based identity and access management platform provided by Microsoft that allows organizations to authenticate and authorize users and applications. We will discuss the benefits of using Azure AD for OAuth, how to set up an Azure AD tenant and register an application, and how to use OAuth to authenticate users and authorize access to Web APIs representing the entry point to those Micro-services.
Registering a Web API in Azure AD
A Web API requires a URI scope. That is a unique ID for identifying the API. It could be the URL where the API is hosted or any other URN. Keep in mind two things when assigning this scope to an API.
You can associate Roles to an App Registration representing a Web API. Those roles are not OAuth scopes because they are not requested or consented to by the users. They are assigned by the owner of the Web API in Azure AD to one or more users or other app registrations. Azure AD can evaluate who is the caller application or user and assign the corresponding roles in the Web API. When that happens, Azure AD injects the roles in the access token (JWT) using the roles attribute.
You can define two types of Roles, Application Roles and User Roles, also known as Delegated Roles.
Application Roles only work for the Client Credentials flow where Authorization is performed in the context of the caller application. The Delegated Roles are used for the rest of OAuth flows and OpenID Connect. For Micro-Services, we are only interested in the former.
Many of the roles that Microsoft defined for the Azure APIs use the following naming convention, <resource>.<action>, which permits you to do <action> on <resource>. For instance, Users.ReadWrite on the Graph API allows any client application to read or update users with that API.
Roles are not global. As we said before, they are defined in the context of a Web API. Users.Write does not mean anything for Azure if it is not passed in the context of the Graph API. Azure AD uses the URI scope to identify the API, and then looks for the requested role in that API only.
Display Name: Admin Role
Allowed member types: Applications
Value: Admin
Description: Allows executing any operation on the web api
Display Name: Read-Only Role
Allowed member types: Applications
Value: ReadOnly
Description: Allows read-only access on the web api
After following all these steps, you should have a new Web API that supports two roles, Admin and ReadOnly.
Recommended by LinkedIn
Registering a Client Application in Azure AD for your Web API
A client application is the counter part of a Web API. It is another App Registration, but used for requesting an access token for a Web API.
You must provide the following details for a client application.
Take note of the "Value" for the new secret, as you won't be able to retrieve in the future.
"Value" is the new client secret. The client id is the App ID associated with the App Registration. Ignore the Secret ID, which is not really used at all for authentication.
Getting an access token for the Web API
Getting a new fresh access token or JWT from Azure AD with client credentials requires an HTTP POST with the following details,
URL: https://login.microsoftonline.com/{tenant}//oauth2/v2.0/toke
Content-Type: application/x-www-form-urlencoded
Body:
client_id={client id}
&scope={web api uri scope}/.default
&client_secret={client secret}
&grant_type=client_credentials
Tenant: It's the tenant ID where the App Registration for the Web API and Client were created.
Client Id: It's the App Id assigned to the App Registration created for the client.
Scope: It's the Web API URI scope assigned to the Web API. Azure AD uses a strange convention for this. When you use client credentials, "/.default" must be appended to the URI scope. No other value is allowed. This scope is used by Azure AD to determine which API the client application wants to consume and generate an access token for it. This will drive the generation of the JWT and how some of its attributes are set. For instance, the audience or AUD attribute will be the set with the App ID for the Web API, and roles attribute with all the roles assigned to the client in that given API. For our example, the complete value would be api://myapis/MyWebAPI/.default
Client Secret: It's the secret assigned to the App Registration for the client.
An access token for a Web API looks as follow.
{
"aud": "api://myapis/MyWebAPI",
"iss": "https://sts.windows.net/f6aba1d9-da41-4ea3-8f30-4970725586b9/",
"iat": 1672924530,
"nbf": 1672924530,
"exp": 1672928430,
"aio": "E2ZgYHD9N/XE5ot5He2iV1Z1VfAyAAA=",
"appid": "55b2a7ec-73f3-45c2-af08-21ecc33dc40e",
"appidacr": "1",
"idp": "https://sts.windows.net/f6aba1d9-da41-4ea3-8f30-4970725586b9/",
"oid": "1f3086f6-9164-45f2-b479-a93f64d1006a",
"rh": "0.AX0A2aGr9kHao06PMElwclWGqGL1t9EZOxJMkZf3CB4gW3ucAAA.",
"roles": [
"Admin"
],
"sub": "1f3086f6-9164-45f2-b479-a93f64d1007a",
"tid": "f6aba1d9-da41-4ea3-8f30-4970725586a8",
"uti": "G-1ZiIhWbkKJdvkZfawxAA",
"ver": "1.0"
}
aud: The audience for the access token. This is used by the Web API to validate that the token was actually issued for it.
iss: The issuer ID. This is an unique identifier for the Azure AD tenant.
sub: The client ID
roles: The roles assigned to the client on the Web API.
Wrapping Up
At this point, you should have a solid understanding of how OAuth and Azure AD can be used to secure your micro-services architecture using the OAuth client credentials flow, and be equipped with the knowledge to implement it in your own projects.