Restricting API Management access to users through AAD

Published Feb 14 2021 12:37 AM 3,125 Views
Microsoft

API Management has the ability to validate a JSON Web Token (JWT) through the validate-jwt policy. If you use the OpenID config URI property in the policy and set it to your AAD tenant's OpenID Connect metadata document endpoint, the token would be validated for anyone in your tenant. The goal in this blog post is to control that access to only allow specific users, using an app registration, roles and claims. I will outline the steps to set this up below.

  1. Create an App registration in your Azure Active Directory.
    2021-02-06_10-49-28.png

    Enter a name and Register, leave the other settings as they are for now.

  2. Open the App registration and go to the "App roles | Preview" blade. Create a role. For our purposes, we can use:
    2021-02-06_10-50-19.png

     

  3. Go to the "Expose an API" blade and set the Application ID URI:
    2021-02-06_10-52-26.png

     

  4. Now, try to get a token for this resource using Azure CLI:
    az login
    az account get-access-token --resource api://a268af9e-1598-4ec3-ad16-77e30b042f92

  5. Notice you will get a HTTP 400 error that states that Azure CLI is not an authorized client application. To fix this, go back to the "Expose an API" blade in the app registration and add a scope:
    2021-02-06_10-54-05.png

     

  6. Now, we can add the client application from the same blade. Use the client ID for Azure CLI from the error message on step 5. The error looks like this: "Get Token request returned http error: 400 and server response: {"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named 'Microsoft Azure CLI'."
    2021-02-06_10-54-43.png

     

  7. Next, we need to grant a user that role to test. Go to the "Overview" blade in your app registration and copy the Application (Client) ID. Then go back to Azure Active Directory, "Enterprise applications" blade and search for the Application ID. Open the enterprise application corresponding to your App registration.

  8. From the "Users and groups" blade, add yourself as a user and select the role you created on step 2:
    2021-02-06_10-55-47.png

     

  9. Now we can try to generate a token from Azure CLI again:
    az account get-access-token --resource api://a268af9e-1598-4ec3-ad16-77e30b042f92'

  10. Copy that token and decode it using https://jwt.ms:
    2021-02-06_10-48-38.png

     

    Notice the audience (aud) is your Application ID URI generated on step 3 and there is a "roles" claim with the role we assigned to ourselves on step 2. We will use this information in the API Management validate-jwt policy to restrict access to tokens that have been generated for this audience and have this specific role. Any other tokens would not be validated by API Management.

  11. Go to API Management and create an API. We can use Http Bin to test:
    2021-02-06_10-47-33.png

     

  12. Create an operation:
    2021-02-06_10-46-30.png

     

  13. Edit the policy for the operation we created:
    2021-02-06_10-45-39.png

  14. The policy would look something like this:
    <policies>
        <inbound>
            <validate-jwt header-name="Authorization" failed-validation-httpcode="403" failed-validation-error-message="Forbidden">
                <openid-config url="https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0/.well-known/openid-configuration" />
                <audiences>
                    <audience>api://a268af9e-1598-4ec3-ad16-77e30b042f92</audience>
                </audiences>
                <issuers>
                    <issuer>https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/</issuer>
                </issuers>
                <required-claims>
                    <claim name="roles" match="any">
                        <value>APIM.Access</value>
                    </claim>
                </required-claims>
            </validate-jwt>
            <base />
        </inbound>
        <backend>
            <base />
        </backend>
        <outbound>
            <base />
        </outbound>
        <on-error>
            <base />
        </on-error>
    </policies>​

    Note: you can get the value for the OpenID config URL from the Azure Portal by going to Azure Active Directory -> App registrations -> Endpoints -> OpenID Connect metadata document
    Note 2: The audience is the Application ID URI from Step 3.
    Note 3: The value of the "roles" claim is the value of the role we created at Step 2.
    Note 4: I added the issuer from the token I got from Azure CLI as well (you can see the value if you decode it - Step 10).

  15. Now, let's test it using Postman. Generate another token using Azure CLI if the previous one expired (Step 9). Select your API Management operation in the Azure Portal and go to the Test tab to get the Request URL and your subscription key:
    2021-02-06_10-44-55.png

  16. Open postman and fill in the token in the Authorization header (including "Bearer " in front of the value), the subscription key if it's required, then send a request:
    2021-02-06_10-43-13.png

     

If you get a HTTP 403 and/or you need to debug, the OCP trace feature would be helpful. If you include the subscription key and the Ocp-Apim-Trace: true header, the response will contain a link to a trace file in the header Ocp-Apim-Trace-Location which will show what is going wrong.

 

2021-02-06_11-00-02.png

For more information, these links would come in handy:
https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-restrict-your-app-to-a-set-of-...

https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps

https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#Val...

 

 

 

 

 

1 Comment
Occasional Visitor

 Great post! If I want to acquire the same access token from a web app for the login user,  how can it be achieved? I mean to use javascript code to do the same thing as "az account get-access-token".Thanks!

@vladd

%3CLINGO-SUB%20id%3D%22lingo-sub-2116259%22%20slang%3D%22en-US%22%3ERestricting%20API%20Management%20access%20to%20users%20through%20AAD%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2116259%22%20slang%3D%22en-US%22%3E%3CP%3EAPI%20Management%20has%20the%20ability%20to%20validate%20a%20JSON%20Web%20Token%20(JWT)%20through%20the%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fapi-management%2Fapi-management-access-restriction-policies%23ValidateJWT%22%20rel%3D%22noopener%20noreferrer%22%20target%3D%22_blank%22%3Evalidate-jwt%20policy%3C%2FA%3E.%20If%20you%20use%20the%20OpenID%20config%20URI%20property%20in%20the%20policy%20and%20set%20it%20to%20your%20AAD%20tenant's%20OpenID%20Connect%20metadata%20document%20endpoint%2C%20the%20token%20would%20be%20validated%20for%20anyone%20in%20your%20tenant.%20The%20goal%20in%20this%20blog%20post%20is%20to%20control%20that%20access%20to%20only%20allow%20specific%20users%2C%20using%20an%20app%20registration%2C%20roles%20and%20claims.%20I%20will%20outline%20the%20steps%20to%20set%20this%20up%20below.%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3ECreate%20an%20App%20registration%20in%20your%20Azure%20Active%20Directory.%3CBR%20%2F%3E%3CP%3EEnter%20a%20name%20and%20Register%2C%20leave%20the%20other%20settings%20as%20they%20are%20for%20now.%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3EOpen%20the%20App%20registration%20and%20go%20to%20the%20%22App%20roles%20%7C%20Preview%22%20blade.%20Create%20a%20role.%20For%20our%20purposes%2C%20we%20can%20use%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3EGo%20to%20the%20%22Expose%20an%20API%22%20blade%20and%20set%20the%20Application%20ID%20URI%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ENow%2C%20try%20to%20get%20a%20token%20for%20this%20resource%20using%20Azure%20CLI%3A%3CBR%20%2F%3Eaz%20login%3CBR%20%2F%3Eaz%20account%20get-access-token%20--resource%20api%3A%2F%2Fa268af9e-1598-4ec3-ad16-77e30b042f92%3CBR%20%2F%3E%3CBR%20%2F%3E%20Notice%20you%20will%20get%20a%20HTTP%20400%20error%20that%20states%20that%20Azure%20CLI%20is%20not%20an%20authorized%20client%20application.%20To%20fix%20this%2C%20go%20back%20to%20the%20%22Expose%20an%20API%22%20blade%20in%20the%20app%20registration%20and%20add%20a%20scope%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ENow%2C%20we%20can%20add%20the%20client%20application%20from%20the%20same%20blade.%20Use%20the%20client%20ID%20for%20Azure%20CLI%20from%20the%20error%20message%20on%20step%205.%20The%20error%20looks%20like%20this%3A%20%22Get%20Token%20request%20returned%20http%20error%3A%20400%20and%20server%20response%3A%20%7B%22error%22%3A%22invalid_grant%22%2C%22error_description%22%3A%22AADSTS65001%3A%20The%20user%20or%20administrator%20has%20not%20consented%20to%20use%20the%20application%20with%20ID%20'04b07795-8ddb-461a-bbee-02f9e1bf7b46'%20named%20'Microsoft%20Azure%20CLI'.%22%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ENext%2C%20we%20need%20to%20grant%20a%20user%20that%20role%20to%20test.%20Go%20to%20the%20%22Overview%22%20blade%20in%20your%20app%20registration%20and%20copy%20the%20Application%20(Client)%20ID.%20Then%20go%20back%20to%20Azure%20Active%20Directory%2C%20%22Enterprise%20applications%22%20blade%20and%20search%20for%20the%20Application%20ID.%20Open%20the%20enterprise%20application%20corresponding%20to%20your%20App%20registration.%3CBR%20%2F%3E%3CBR%20%2F%3E%20From%20the%20%22Users%20and%20groups%22%20blade%2C%20add%20yourself%20as%20a%20user%20and%20select%20the%20role%20you%20created%20on%20step%202%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ENow%20we%20can%20try%20to%20generate%20a%20token%20from%20Azure%20CLI%20again%3A%3CBR%20%2F%3Eaz%20account%20get-access-token%20--resource%20api%3A%2F%2Fa268af9e-1598-4ec3-ad16-77e30b042f92'%3CBR%20%2F%3E%3CBR%20%2F%3E%20Copy%20that%20token%20and%20decode%20it%20using%20%3CA%20href%3D%22https%3A%2F%2Fjwt.ms%3A%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Fjwt.ms%3A%3C%2FA%3E%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ENotice%20the%20audience%20(aud)%20is%20your%20Application%20ID%20URI%20generated%20on%20step%203%20and%20there%20is%20a%20%22roles%22%20claim%20with%20the%20role%20we%20assigned%20to%20ourselves%20on%20step%202.%20We%20will%20use%20this%20information%20in%20the%20API%20Management%20validate-jwt%20policy%20to%20restrict%20access%20to%20tokens%20that%20have%20been%20generated%20for%20this%20audience%20and%20have%20this%20specific%20role.%20Any%20other%20tokens%20would%20not%20be%20validated%20by%20API%20Management.%3CBR%20%2F%3E%3CBR%20%2F%3E%20Go%20to%20API%20Management%20and%20create%20an%20API.%20We%20can%20use%20Http%20Bin%20to%20test%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3ECreate%20an%20operation%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3EEdit%20the%20policy%20for%20the%20operation%20we%20created%3A%3CBR%20%2F%3E%3CBR%20%2F%3E%3CBR%20%2F%3E%20The%20policy%20would%20look%20something%20like%20this%3A%3CBR%20%2F%3E%3CPOLICIES%3E%20%3CINBOUND%3E%20%3CVALIDATE-JWT%20header-name%3D%22%26quot%3BAuthorization%26quot%3B%22%20failed-validation-httpcode%3D%22%26quot%3B403%26quot%3B%22%20failed-validation-error-message%3D%22%26quot%3BForbidden%26quot%3B%22%3E%20%3COPENID-CONFIG%20url%3D%22%26quot%3Bhttps%3A%2F%2Flogin.microsoftonline.com%2F72f988bf-86f1-41af-91ab-2d7cd011db47%2Fv2.0%2F.well-known%2Fopenid-configuration%26quot%3B%22%3E%3C%2FOPENID-CONFIG%3E%20%3CAUDIENCES%3E%20%3CAUDIENCE%3Eapi%3A%2F%2Fa268af9e-1598-4ec3-ad16-77e30b042f92%3C%2FAUDIENCE%3E%20%3C%2FAUDIENCES%3E%20%3CISSUERS%3E%20%3CISSUER%3E%3CA%20href%3D%22https%3A%2F%2Fsts.windows.net%2F72f988bf-86f1-41af-91ab-2d7cd011db47%2F%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fsts.windows.net%2F72f988bf-86f1-41af-91ab-2d7cd011db47%2F%3C%2FA%3E%3C%2FISSUER%3E%20%3C%2FISSUERS%3E%20%3CREQUIRED-CLAIMS%3E%20%3CCLAIM%20name%3D%22%26quot%3Broles%26quot%3B%22%20match%3D%22%26quot%3Bany%26quot%3B%22%3E%20%3CVALUE%3EAPIM.Access%3C%2FVALUE%3E%20%3C%2FCLAIM%3E%20%3C%2FREQUIRED-CLAIMS%3E%20%3C%2FVALIDATE-JWT%3E%20%3CBASE%20%2F%3E%20%3C%2FINBOUND%3E%20%3CBACKEND%3E%20%3CBASE%20%2F%3E%20%3C%2FBACKEND%3E%20%3COUTBOUND%3E%20%3CBASE%20%2F%3E%20%3C%2FOUTBOUND%3E%20%3CON-ERROR%3E%20%3CBASE%20%2F%3E%20%3C%2FON-ERROR%3E%20%3C%2FPOLICIES%3E%20%3CBR%20%2F%3ENote%3A%20you%20can%20get%20the%20value%20for%20the%20OpenID%20config%20URL%20from%20the%20Azure%20Portal%20by%20going%20to%20Azure%20Active%20Directory%20-%26gt%3B%20App%20registrations%20-%26gt%3B%20Endpoints%20-%26gt%3B%26nbsp%3BOpenID%20Connect%20metadata%20document%3CBR%20%2F%3ENote%202%3A%20The%20audience%20is%20the%20Application%20ID%20URI%20from%20Step%203.%3CBR%20%2F%3ENote%203%3A%20The%20value%20of%20the%20%22roles%22%20claim%20is%20the%20value%20of%20the%20role%20we%20created%20at%20Step%202.%3CBR%20%2F%3ENote%204%3A%20I%20added%20the%20issuer%20from%20the%20token%20I%20got%20from%20Azure%20CLI%20as%20well%20(you%20can%20see%20the%20value%20if%20you%20decode%20it%20-%20Step%2010).%3CBR%20%2F%3E%3CBR%20%2F%3E%20Now%2C%20let's%20test%20it%20using%20Postman.%20Generate%20another%20token%20using%20Azure%20CLI%20if%20the%20previous%20one%20expired%20(Step%209).%20Select%20your%20API%20Management%20operation%20in%20the%20Azure%20Portal%20and%20go%20to%20the%20Test%20tab%20to%20get%20the%20Request%20URL%20and%20your%20subscription%20key%3A%3CBR%20%2F%3E%3CBR%20%2F%3E%3CBR%20%2F%3E%20Open%20postman%20and%20fill%20in%20the%20token%20in%20the%20Authorization%20header%20(including%20%22Bearer%20%22%20in%20front%20of%20the%20value)%2C%20the%20subscription%20key%20if%20it's%20required%2C%20then%20send%20a%20request%3A%3CBR%20%2F%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIf%20you%20get%20a%20HTTP%20403%20and%2For%20you%20need%20to%20debug%2C%20the%20OCP%20trace%20feature%20would%20be%20helpful.%20If%20you%20include%20the%20subscription%20key%20and%20the%20Ocp-Apim-Trace%3A%20true%20header%2C%20the%20response%20will%20contain%20a%20link%20to%20a%20trace%20file%20in%20the%20header%26nbsp%3BOcp-Apim-Trace-Location%20which%20will%20show%20what%20is%20going%20wrong.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%3C%2FP%3E%3CP%3EFor%20more%20information%2C%20these%20links%20would%20come%20in%20handy%3A%3CBR%20%2F%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Fhowto-restrict-your-app-to-a-set-of-users%22%20rel%3D%22noopener%20noreferrer%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Fhowto-restrict-your-app-to-a-set-of-users%3C%2FA%3E%3C%2FP%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Fhowto-add-app-roles-in-azure-ad-apps%22%20rel%3D%22noopener%20noreferrer%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Fhowto-add-app-roles-in-azure-ad-apps%3C%2FA%3E%3C%2FP%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fapi-management%2Fapi-management-access-restriction-policies%23ValidateJWT%22%20rel%3D%22noopener%20noreferrer%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fapi-management%2Fapi-management-access-restriction-policies%23ValidateJWT%3C%2FA%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-2116259%22%20slang%3D%22en-US%22%3E%3CP%3ECreating%20an%20app%20in%20AAD%20that%20represents%20API%20Management%20and%20only%20allow%20users%20that%20have%20right%20app%20roles%20in%20the%20claims%20when%20validating%20the%20Bearer%20token.%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-SUB%20id%3D%22lingo-sub-2350117%22%20slang%3D%22en-US%22%3ERe%3A%20Restricting%20API%20Management%20access%20to%20users%20through%20AAD%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2350117%22%20slang%3D%22en-US%22%3E%3CP%3E%26nbsp%3BGreat%20post!%26nbsp%3B%3C%2FP%3E%3CP%3EIf%20I%20want%20to%20acquire%20the%20same%20access%20token%20from%20a%20web%20app%20for%20the%20login%20user%2C%26nbsp%3B%20how%20can%20it%20be%20achieved%3F%3C%2FP%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F956580%22%20target%3D%22_blank%22%3E%40vladd%3C%2FA%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E
Co-Authors
Version history
Last update:
‎Feb 06 2021 01:03 AM
Updated by: