Unable to get bearer token from Azure AD to use with API App

10,875

I have gone ahead and followed the tutorial to which you referred: Call an Azure API app from a web app client authenticated by Azure Active Directory

  1. Create an Azure API Api that returns contact data
  2. Deploy the API App to Azure App Service.
  3. Secure the API App using Azure Active Directory.

Then I was able to retrieve the token, and as you can see from the following demo, my code is no different than your code, except that it using a later version of the using Microsoft.IdentityModel.Clients.ActiveDirectory libraries that use Async.

Get access token from AAD

class Program
{
    static void Main(string[] args)
    {
        var authContext = new AuthenticationContext(Constants.AUTHORITY);
        var credential = 
            new ClientCredential(Constants.CLIENT_ID, Constants.CLIENT_SECRET);
        var result = (AuthenticationResult)authContext
            .AcquireTokenAsync(Constants.API_ID_URL, credential)
            .Result;
        var token = result.AccessToken;
        Console.WriteLine(token.ToString());
        Console.ReadLine();
    }
}

Constants

AUTHORITY. The first segment of this is https://login.microsoftonline.com. The final segment is an allowed tenant. We set the allowed tenant at portal.azure.com, going to the Gateway for our application, and choosing Settings > Identity > Azure Active Directory > Allowed Tenants. My tenant is bigfontoutlook.onmicrosoft.com.

CLIENT_ID. We retrieve this client id from the application that we added to Azure Active Directory. Find this at manage.windowsazure.com > Active Directory > Your Directory > APPLICATIONS > Your Application > CONFIGURE. Once we have retrieved it, we must add it to our Gateway's Azure Active Directory settings in the Client ID field.

CLIENT_SECRET. We create/retrieve this in the same location that we retrieve our client id.

API_ID_URL. We retrieve this within the Gateway blade for our Web API App by choosing Settings > Identity > Azure Active Directory > App URL.

Here are the ones that work for me.

class Constants
{
    public const string AUTHORITY =
     "https://login.microsoftonline.com/bigfontoutlook.onmicrosoft.com/";

    public const string CLIENT_ID = 
      "0d7dce06-c3e3-441f-89a7-f828e210ff6d";

    public const string CLIENT_SECRET =
      "AtRMr+Rijrgod4b9Q34i/UILldyJ2VO6n2jswkcVNDs=";

    public const string API_ID_URL = 
      "https://mvp201514929cfaaf694.azurewebsites.net/login/aad";
}

Final decoded JWT

This is what the decoded JWT access token contains.

{
 typ: "JWT",
 alg: "RS256",
 x5t: "MnC_VZcATfM5pOYiJHMba9goEKY",
 kid: "MnC_VZcATfM5pOYiJHMba9goEKY"
}.
{
 aud: "https://mvp201514929cfc350148cfa5c9b24a7daaf694.azurewebsites.net/login/aad",
 iss: "https://sts.windows.net/0252f597-5d7e-4722-bafa-0b26f37dc14f/",
 iat: 1442346927,
 nbf: 1442346927,
 exp: 1442350827,
 ver: "1.0",
 tid: "0252f597-5d7e-4722-bafa-0b26f37dc14f",
 oid: "5a6f33eb-b622-4996-8a6a-600dce355389",
 sub: "5a6f33eb-b622-4996-8a6a-600dce355389",
 idp: "https://sts.windows.net/0252f597-5d7e-4722-bafa-0b26f37dc14f/",
 appid: "0d7dce06-c3e3-441f-89a7-f828e210ff6d",
 appidacr: "1"
}.

Note: It's a throwaway app in a throwaway active directory account with a throwaway resource group, so showing my security credentials is a non-issue.

Diagram just to be sure :)

Connecting the dots with Azure ADD

Share:
10,875
trailmax
Author by

trailmax

tech.trailmax.info - My blog. I write about C# and ASP.Net MVC (and sometimes about other random stuff) hrnet.trailmax.info My other blog. There I write about a technology I have been exposed for longer than I wanted. And now I know too much about HR.Net. This is part of my consulting service.

Updated on June 19, 2022

Comments

  • trailmax
    trailmax almost 2 years

    I have an MVC application that needs to access private API App in Azure that is protected with Azure AD authentication. So I need to get Azure AD bearer token, transfer it into Zumo-Auth token and use it to access the API App.

    I'm going through this tutorial and everything is working fine until the point when I need to request the token from authContext. Here is the snippet of a code:

    var authContext = new AuthenticationContext(
        "https://login.microsoftonline.com/MyADDomain.onmicrosoft.com");
    
    ClientCredential credential = new ClientCredential(
        "04472E33-2638-FAKE-GUID-F826AF4928DB", 
        "OMYAPIKEY1x3BLAHEMMEHEHEHEHEeYSOMETHINGRc=");
    
    // Get the AAD token.
    var appIdUri = 
        "https://MyAppGateway-814485545465FAKE4d5a4532cd.azurewebsites.net/login/aad";
    
    //var appIdUri = "https://MyADDomain.onmicrosoft.com/MyAppName";
    //var appIdUri = "https://MyADDomain.onmicrosoft.com/";
    //var appIdUri = "https://graph.windows.net";
    
    AuthenticationResult result = 
        authContext.AcquireToken(appIdUri, credential); // <-- can't get the token from AD
    
    // downhill from here
    var aadToken = new JObject();
    aadToken["access_token"] = result.AccessToken;
    var appServiceClient = new AppServiceClient(
        "https://MyAppGateway-814485545465FAKE4d5a4532cd.azurewebsites.net/");
    
    // Send the AAD token to the gateway and get a Zumo token
    var appServiceUser = await appServiceClient.LoginAsync("aad", aadToken);
    

    The line with authContext.AcquireToken(appIdUri, credential) is the one causing trouble.

    If as appIdUri I give https://MyAppGateway-814485545465FAKE4d5a4532cd.azurewebsites.net/login/aad, I get exception:

    400: AdalServiceException: AADSTS50001: Resource 'https://MyAppGateway-814485545465FAKE4d5a4532cd.azurewebsites.net/login/aad' is not registered for the account.

    But this exact line is in the list of Reply Url in the AD Application

    Reply URI in Azure AD Application

    When I try to use https://MyADDomain.onmicrosoft.com/MyAppName or https://MyADDomain.onmicrosoft.com/ as appIdUri I get a different exception message:

    400: AdalServiceException: AADSTS50105: Application '04472E33-2638-FAKE-GUID-F826AF4928DB' is not assigned to a role for the application 'https://MyADDomain.onmicrosoft.com/MyAppName'

    Or

    400: AdalServiceException: AADSTS50105: Application '04472E33-2638-FAKE-GUID-F826AF4928DB' is not assigned to a role for the application 'https://MyADDomain.onmicrosoft.com/'

    In both cases I had the App ID URI in the AD Application set to 'https://MyADDomain.onmicrosoft.com/MyAppName' or 'https://MyADDomain.onmicrosoft.com/'. And both of the names in the list of Reply URL.

    Eventually after enough tries I have put https://graph.windows.net as appIdUri and got the bearer token back. But the token was given with expiry date in the past (about 1 minute in the past). So I could not do anything further with this. And got 401-Unauthenticated when tried to use the token to login into API App.

    What am I missing?

  • trailmax
    trailmax over 8 years
    Thanks for this, I'll give it a go tomorrow.
  • trailmax
    trailmax over 8 years
    You know wha? I copied your code, put my guids, secrets and urls. And got the same damned exception again! Then I created a whole new Api app and a new AD Application. Copied the credentials and everything worked. Not sure what exactly was broken, but I suspect AD App was messed up beyond recognition.
  • Shaun Luttin
    Shaun Luttin over 8 years
    Yup. Sometimes troubleshooting makes a mess :)
  • Jaxidian
    Jaxidian over 7 years
    I just want to add that I ran into this exact same problem with the exact same errors and the exact same solution (start over and try again). The thing is, I've now started over and tried again about 8 times and only 5 of the 8 times has it worked. Something seems to be unreliable with this - I don't know if this is due to the Preview'ness of the tooling or what, but it doesn't seem to be terribly reliable. :-/
  • Shaun Luttin
    Shaun Luttin over 7 years
    @Jaxidian I was facing something like that once with OAuth, and it turned out to be the difference between using HTTP and HTTPS. Have you checked that?
  • Jaxidian
    Jaxidian over 7 years
    @ShaunLuttin Thanks. Fortunately (unfortunately?) I figured out the unpredictable behavior. It's predictable once you account for delayed engagement of the "User assignment required?" setting. With that enabled (and waiting a minute), it 100% fails with the OP's error. No matter what, I cannot seem to figure out how to "assign" the client application to the web api application.