Search code examples
azureasp.net-web-apiadal

AcquireTokenAsync returns "invalid_grant", "AADSTS65001"


I'm trying to call an Azure Hosted WebAPI from a site-based WebService. The site based service is an adal-angluar SPA which must then call out to a rest API hosted in Azure. The bearer token from AAD is successfully passed to the site based web API, and this then must get a new token on behalf of the user to call the downstream API, as per this example:
https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof

The downstream API in the example is https://graph.windows.net. And this is passed into the AcquireTokenAsync call as the resourceId.

In my case, the downstream API is an Azure app that I am writing, so I have full control over it.

The problem I have is 'invalid_grant' is returned from the AcquireTokenAsync call in my site based api that is trying to get a new token on behalf of the logged in user.

The site based web app has an appid created in Azure, and attempts to get a new token as follows:

var appId = ConfigurationManager.AppSettings["ActiveDirectoryApplicationId"];
var appKey = ConfigurationManager.AppSettings["ActiveDirectoryApplicationKey"];
var aadInstance = ConfigurationManager.AppSettings["ActiveDirectoryInstance"];
var tenant = ConfigurationManager.AppSettings["ActiveDirectoryTenant"];
var onboardingResourceId = ConfigurationManager.AppSettings["OnboardingApplicationResourceId"];

var clientCredential = new ClientCredential(appId, appKey);
var bootstrapContext =
            ClaimsPrincipal.Current.Identities.First().BootstrapContext as
                System.IdentityModel.Tokens.BootstrapContext;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var userAccessToken = bootstrapContext.Token;
var userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
var authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, aadInstance, tenant);

var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var authContext = new AuthenticationContext(authority, new TokenCache());

var result = await authContext.AcquireTokenAsync(onboardingResourceId, clientCredential, userAssertion);
var accessToken = result.AccessToken;
return accessToken;

So my questions are, what security needs to be implemented in Azure to get the token? I've read that the Application manifest of the downstream API needs to be updated to include the site based app as a "knownClientApplication". Is that correct?

What should my resourceId look like for my downstream web api?

Can I test all of this without deploying my downstream Web Api to Azure? I want to be able to debug all of this locally including the security.

Thanks.


Solution

  • To give a custom application permissions to call another application on behalf of a user, you need to follow these steps:

    1. Find the custom application in the old portal and open the configuration page.
    2. Click on Add application at the bottom of the screen.
    3. Select All Apps Search for the app names you wish to give access to, then add by clicking the tick.
    4. Select 'Delegated permissions' in the permissions section for the application 5. List item
    5. Click save.