1
votes

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.

2
Make sure you have a trailing slash for your audience (aud), like so https://onboardingResourceId.example.com/.evilSnobu

2 Answers

0
votes

The knownClientApplications array contains the client ids of applications that are, well, known clients. What it means is that when the user first consents for your front-end app, it will also show the API's permission requirements in addition to the front-end app's requirements. You do not need to consent the API separately, which is usually unwanted. That way they are both consented and permissions granted right away.

The resource id you can find in the Azure Portal. Just find the app for your API, go to Properties, and find the App ID URI:

Azure AD App Properties

Typically the App ID URI (or resource URI) is of the form https://your-domain-name.com/AppName. The important bit for multi-tenant applications is that the domain in the URI must be a verified domain in Azure AD.

And yes, you can test everything in a local environment. As long as you specify reply URLs as localhost etc. you should be good to go.

Sorry to say I am not really sure why you are getting an invalid grant error.

0
votes

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.