Secure REST API with OAuth 2.0 Client Credentials Flow using Azure AD. Part 1
Introduction
The following post will describe how to secure Spring Boot REST API with OAuth2 2.0 Client Credentials Flow (M2M) using Azure AD as Authorization Server. The focus will be on Azure AD setup and related Spring Boot/Spring Security configuration nuances. The post will be divided into 2 parts:
Both parts are based on misc documentation resources, tutorials and examples provided by Microsoft, Spring and https://www.baeldung.com/. The links will be provided in References section.
Part 1. Overview and Azure AD setup
Overview
OAuth2 2.0 Client Credentials Flow (M2M) is intended to cover Machine-to-Machine (M2M) authentication when a human interaction is not available or applicable (ex: a scheduled job calls a secured api). The flow includes 3 parties ( Authorization Server, Resource Server and Client) and contains the following major steps:
-
Clientsends request to Authorization Server to get an access token. Client is usingClient IDandClient Secret(as credentials) and providesScopeof the request.Scopeidentifies the resource/access the client is trying to get. -
Authorization Serverauthenticates theClientand provides back anaccess token. -
Clientcalls theResource Serverand providesaccess tokenas a part of the request. -
Resource Serververifiesaccess tokenand provides access to the requested resource.
This Example/Approach
1. Authorization Server
Will be using Microsoft Azure Active Directory (Azure AD) as Authorization Server. Azure AD supports OAuth2 2.0 Client Credentials Flow and provides all the necessary configuration options.
2. Resource Server
The example will have a Spring Boot based REST API with 2 endpoints. Will be using Spring Security OAuth 2.0 Resource Server to protect the API and integrate with the Authorization Server.
3. Client
Will be using Curl as our HTTP client to demonstrate that our approach is pure HTTP based, compliant with OAuth 2.0 and client technology agnostic.
Azure AD Setup
API Registration
-
Create API (Resource Server) registration in Azure AD by following steps in https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app. Please, note that step with
Redirect URIis optional - no need to provide anything there. -
Setup permissions (Application Roles) for API by modifying
appRolessection of App Manifest file:
...
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Consumer apps have access to Hi Endpoint",
"displayName": "CallHelloApiRole",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "CallHiApiRole"
},
{
"allowedMemberTypes": [
"Application"
],
"description": "Consumer apps have access to Hello Endpoint",
"displayName": "CallHiApiRole",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "CallHelloApiRole"
}
],
...Notes:
- Two app roles were setup (
CallHiApiRoleandCallHiApiRole) so they can be granted separately if needed Manifestis available viaAzure AD->App Registrations-><Your App>->Manifest.Idparam in role setup must be a GUID and you will need to generate it manually (ex: using https://www.guidgenerator.com/)Valueparam contains the name of the role/permission
Client Registration
-
Register Client App in Azure AD by following steps in https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app. These are the same steps as in step #1 of
API Registration. - Grant API permissions (App Roles) to Client App Registration using
Azure AD->App Registrations-><Your Client>->API Permissionsscreen:- Click
Add a permissionbutton - Select
My APIs-><Your API>->Application Permissions - Tick
CallHiApiRoleandCallHelloApiRole
- Click
-
Provide Admin consent to Client for new permissions in
Azure AD->App Registrations-><Your Client>->API Permissionsscreen by clickingGrant admin consent for <Your Azure AD Instance>button. - Create Client Secret by using
Azure AD->App Registrations-><Your Client>->Certificates and secretsscreen by clickingNew client secret.
Verify Client and API Registration
Will be checking the setup by performing a Request Token call to the Authorization Server (Azure AD):
Request
curl --request POST \
--url https://login.microsoftonline.com/<azure_ad_tenant_id>/oauth2/v2.0/token \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data scope=api%3A%2F%2F<api_application_id>%2F.default \
--data client_id=<client_id> \
--data 'client_secret=<client_secret>'Notes:
<azure_ad_tenant_id>is the Tenant ID of your Azure AD instance (<Azure AD->Overview>)- Scope: the scope defines requested scope. For Azure AD the format is
api://<api_application_id>/.default <api_application_id>is the Application ID of the API (Azure AD->App Registrations-><Your App>-<Overview>)<client_id>is the Application ID of the Client registration in AD (Azure AD->App Registrations-><Your Client>->Overview)<client_secret>is the secret defined for Client in step #4 ofClient Registrationstep
Response
Will be getting a JWT access token as our response (successful), ex.:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "CtTuhMJmD5M7DLdzD2v2x3QKSRY",
"kid": "CtTuhMJmD5M7DLdzD2v2x3QKSRY"
}.{
"aud": "api://<api_application_id>",
"iss": "https://sts.windows.net/<azure_ad_tenant_id>/",
"iat": 1587765136,
"nbf": 1587765136,
"exp": 1587769036,
"aio": "...",
"appid": "...",
"appidacr": "1",
"idp": "https://sts.windows.net/<azure_ad_tenant_id>/",
"oid": "...",
"roles": [
"CallHelloApiRole",
"CallHiApiRole"
],
"sub": "...",
"tid": "<azure_ad_tenant_id>",
"uti": "...",
"ver": "1.0"
}.[Signature]Notes:
audclaim contains the audience which is your API URIrolesclaim contains list of granted permissions for the client for the request scope