Azure Active Directory: web api call another web api - c#

I have two different Web API applications (web-api-app1 and web-api-app2) integrated with Azure Active Directory authentication.
I am able to call both API applications individually using some console application like this:
AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/[AD Tenent]");
var clientCredentials = new ClientCredential("app id", "app key");
AuthenticationResult ar = ac.AcquireTokenAsync("https://web-api-app1", clientCredentials).Result;
string result = string.Empty;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ar.AccessToken);
HttpResponseMessage response = httpClient.GetAsync("https://web-api-app1/odata").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
}
Console.WriteLine(result);
Now my requirement is that I need to call web-api-app2 from web-api-app1 (web API call to web API call). How do I do that? Thanks!

You need to implement on-behalf-of (docs for v1 endpoints) flow.
app1 should request an access token to app2 using own access token acquired on user authentication.
Manifest of app1 should include permission to access app2.
If apps are in different tenants, the app2 should be consented by app1 tenant admin first.
The official .net example app

Related

Accessing Azure proxy enterprise app external URL from other registered app using ClientSecret in the background

I have following situation
APP1 : Azure enterprise application running through azure proxy. My external URL is https://xxx.msappproxy.net and internal URL for testing purpose I have put is https://reqres.in
Enterprise App is configured to allow login by any user, now this is all applicable when it runs from UI with specific user login, but as I am running the app from the background, I need to create APP2.
APP2 : I need to create another App which has a permission to access (with admin consent in place) enterprise proxy app (APP1). for that I have configured Client secret in APP2, I can successfully get the token from my App
I believe standard method is to set up client secret in APP2, allow permission to access APP1, get the token passing client secret and access APP2.
Following is my code to retrieve token for APP2 and access APP1 external URL.
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create("7cbb265a-cf6d-xxxx-xx-289bd24d0be0")
.WithClientSecret("1eanye2~.-I_K-69i4B8kahdkjasxxxlnr~3v")
.WithAuthority("https://login.microsoftonline.com/877b6ed6-377f-42d5-ab2e-xxxxxxx")
.Build();
string[] scopes = new string[] { "api://7cbb265a-cf6d-4adf-8ae2-xxxxx/.default" };
AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
if(result != null)
{
string Need = result.AccessToken;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {result.AccessToken}");
//HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await httpClient.GetAsync("https://xxx.msappproxy.net/api/users");
string data = await response.Content.ReadAsStringAsync();
}
All works fine and I get the token for APP2, but when I try to access proxy URL with Authorization, HTTPresponseMessage still ask me to login as in response rather then actual data from https://reqres.in/api/users, I am getting whole Azure AD Authentication html page back asking me to authenticate.
I am not sure if I required anything additional configuration to be in done in either APP1 or APP2.
If you can help or give me some direction ahead making this possible would be greatly appreciated.
Regards,
Hiren.
We can give delegate control to other application (app2) and users will be able to access it through app1. please see the below document for more details.
Delegate permissions.
Kindly check these documents 1 and document 2

How to get access token to call MS Graph on behalf of a user in the console app using MSAL?

I have a SPA application that communicates with my backend Web API using AAD v2 authentication. Now I'm developing a console app to call Microsoft Graph on behalf of the user signed into the SPA app.
I have a valid access token of the user (used to call backend Web API). I want to use this access token to request a new token for accessing MS Graph.
Here is the code of the console app for requesting a new access token with MS Graph scopes using MSAL.NET:
string clientId = "<clientId>";
string clientSecret = "<clientSecret>";
string accessToken = "<validAccessTokenForWebApi>";
string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
string[] scopes = new string[] { "User.Read", "Mail.Send" };
string graphAccessToken = null;
try
{
var app = ConfidentialClientApplicationBuilder
.Create(clientId).WithClientSecret(clientSecret).Build();
var userAssertion = new UserAssertion(accessToken, assertionType);
var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion)
.ExecuteAsync().GetAwaiter().GetResult();
graphAccessToken = result.AccessToken;
}
catch (MsalServiceException ex)
{
throw;
}
But when I call app.AcquireTokenOnBehalfOf() I get an exception:
AADSTS50013: Assertion failed signature validation. [Reason - The provided signature value did not match the expected signature value., Thumbprint of key used by client: 'BB839F3453C7C04068B078EDADAB8E6D5F382E76', Found key 'Start=06/04/2019 00:00:00, End=06/04/2021 00:00:00']
What is the reason? What is the right way of getting access token on behalf of a user?
UPDATE - why do I need console app?
I could call Graph API directly from my backend API, but some actions may be delayed by the user (e.g. send mail using Graph API after 30 minutes). That is why I need to do this using the console app that runs on schedule.
If you want to use OAuth 2.0 On-Behalf-Of flow, I think you do not need to develop a console application to call graph api. You can directly use your backend Web API application to acquire access token then call Microsoft Graph. According to my understanding, you just do these steps
Sign-in the user in the client application
Acquire a token to the Web API (TodoListService) and call
it.
The Web API then calls another downstream Web API (The Microsoft
Graph).
For more details, please refer to the sample.
Regarding how to get access token with on behalf flow in the console application, The detailed steps are as below.
Register the web api app
Register APP
Create Client secrets
Configure permissions to access Graph API
Configure an application to expose web APIs(Add scope for the api)
Register the SAP app
Register APP
Create Client secrets
Configure permissions to access web API
Configure known client applications for web API application
In the Azure portal, navigate to your Web api
app registration and click on the Manifest section.
Find the property knownClientApplications and add the Client IDs of the SAP applications
Get access token to call web api
GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?scope=<you web api scope> openid
&redirect_uri=<your sap app redirect url>
&nonce=test123
&client_id=<you sap app client id>
&response_type=id_token token
get access token with on behalf flow
REST API
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=<you web api client id>
&assertion=<you acess token you get in above steps>
&client_secret=<you app secret>
&scope=https://graph.microsoft.com/user.read
&requested_token_use=on_behalf_of
MSAL.net Code
string[] scopes = { "user.read" };
string accesstoken = "";
string appKey = "yor web api client secret";
string clientId = "your web api application id";
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(appKey)
.Build();
UserAssertion userAssertion = new UserAssertion(accesstoken,
"urn:ietf:params:oauth:grant-type:jwt-bearer");
var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync().Result;
Console.WriteLine(result.AccessToken);

How to access web api from another web app which is in same Azure AD?

I have hosted one web api ProductInfo on azure and register that application into Azure AD.
I have another web app UIProduct and this web app want to access the web api ProductInfo both are in same Domain and same Azure AD.
How can I access web api ProductInfo from web app UIProduct ?
is there any token I need to generate again ?
any sample code will be helpful.
code sample taken from this link
After successfully login i'm at Home page then i click on about page where i write this
public async System.Threading.Tasks.Task<ActionResult> About()
{
AuthenticationResult result = null;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, clientSecret);
result = await authContext.AcquireTokenSilentAsync("App ID URI", credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://demotest.azurewebsites.net/api/getdata");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
getting exception - "Failed to acquire token silently as no token was
found in the cache. Call method AcquireToken"
You'll want to go register both apps in the Azure portal both as Web Apps/APIs.
Then in your Web API, set a App ID URI as well as create any scopes you'd like to expose. If this is the only client you'll be using, you may be able to just have an Access scope, but bear in mind this is what end users will see when consenting to your app.
In your web app, you'll then be able to set a Required Permissions for this new web API and scope. This indicates that the client should require consent for this web API and can be granted access tokens for it.
This code sample covers this exact scenario.
The web API basics doc also covers some conceptual info related to this scenario

Web API on-behalf-of adal id_token error

I have created a Web API in Azure.
This Web API makes some calls in SharePoint Online. Some of the api calls are on-behalf-of.
This Web API works fine until 01.05.2018 - and it works fine on old app services, which were created before 01.05.2018.
A microsoft staff member said:
As part of our security hardening efforts we do not allow id_token
redemption for any application created after 2018-05-01 00:00:00.
During the log in process of adal, I got the id_token. The id_token has got the same value as the access_token:
When I call the web api, I will send this token as bearer token.
The Web API takes this token (string accessToken) and starts the method 'AcquireTokenAsync':
var clientID = ConfigurationManager.AppSettings["ClientID"];
var clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
var tenant = ConfigurationManager.AppSettings["Tenant"];
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new AuthenticationContext(
"https://login.microsoftonline.com/" + tenant);
var resource = new Uri(sharePointUrl).GetLeftPart(UriPartial.Authority);
var authResult = await authContext.AcquireTokenAsync(resource, appCred,
new UserAssertion(accessToken));
return authResult.AccessToken;
But in the line which calls 'AcquireTokenAsync' I have got the error message:
AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant
But where is the problem?
The problem is that you use the same application identity in the front-end and back-end, and MS does not allow you to use the Id token (which you use as an access token here because of the former) to get another access token.
A possible solution:
Register another application (the front-end JS app should be a Native app)
It should acquire an access token for your back-end API using either the API's client id or app Id URI as the resource
Then the API can exchange the access token for another access token
If this is a multi-tenant app, the migration is probably not going to be easy.
If it's single-tenant, then all should be possible.
Your front-end app should of course require permission to call your back-end API in Azure AD, and that permission should be granted.
Another solution would be to acquire the other access token in the front-end using ADAL.JS instead of using on-behalf-of in the back-end and attaching that to all API requests in addition to the Id token.

Authenticate to Azure API App using ADAL

I have an Azure API App marked as "Public (authenticated)" and set up an Azure Active Directory identity in the associated gateway as detailed in Protect an API App.
I then created a native application in the same Azure Active Directory Tenant and added permission to access the Gateway in the delegated permissions.
Using ADAL and the following code, I'm able to successfully authenticate and get an access token, but I can't figure out how to use it to access my API app.
string Tenant = "[xxx].onmicrosoft.com";
string Authority = "https://login.microsoftonline.com/" + Tenant;
string GatewayLoginUrl = "https://[gateway].azurewebsites.net/login/aad";
string ClientId = "[native client id]";
Uri RedirectUri = new Uri("[native client redirect url]");
async Task<string> GetTokenAsync()
{
AuthenticationContext context = new AuthenticationContext(Authority);
PlatformParameters platformParams = new PlatformParameters(PromptBehavior.Auto, null);
AuthenticationResult result = await context.AcquireTokenAsync(GatewayLoginUrl, ClientId, RedirectUri, platformParams);
return result.AccessToken;
}
I've tested the API app manually entering an x-zumo-auth header I get in Chrome and it works then, but not with a token I get using ADAL. I've also tried the browser forms described in their sample code which works but doesn't give me a refresh token.
How do I need to set up my authentication code so I can use a TokenCache and ADAL with my API app?
Generally you pass the access token in the Authorization header when when calling a web api:
Authorization: Bearer ThisIsTheAccessTokenYouRecievedFromADAL
You may want to use AppServiceClient to authenticate the user and invoke a protected API App endpoint. Install Microsoft.Azure.AppService SDK (-pre) Nuget package to your client project.
You can find more details in the AzureCards samples on GitHub - https://github.com/Azure-Samples/API-Apps-DotNet-AzureCards-Sample

Categories