Integrating MVC app with Web API, Azure Users Authentication is done using OWIN, Want to remove authentication cookie and pass token in header for api call. how to do it? I use MSAL.cs file for Azure AD authentication. Want to pass token in api call header. first load MVC application page, after authentication call web api methods.
I used following code for azure AD autherization,
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// Extract the code from the response notification
var code = notification.Code;
string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes);
}
catch (Exception ex)
{
//TODO: Handle
throw;
}
}
After the first time sign in users from azure ad using the ASP.Net OpenID Connect OWIN middleware , if you want to call web api , you can add the token to request header :
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string authority = String.Format(CultureInfo.InvariantCulture, Startup.aadInstance, tenantID, string.Empty);
ClientCredential credential = new ClientCredential(Startup.clientSecret);
// Here you ask for a token using the web app's clientId as the scope, since the web app and service share the same clientId.
app = new ConfidentialClientApplication(Startup.clientId, redirectUri, credential, new NaiveSessionCache(userObjectID, this.HttpContext)){};
result = await app.AcquireTokenSilentAsync(new string[] { Startup.clientId });
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, serviceUrl + "/api/todolist");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.Token);
HttpResponseMessage response = await client.SendAsync(request);
Please refer to code sample for more details .
Related
I have a mobile app, which needs to call a REST API. Here is my code:
string url = $#"https://graph.microsoft.com/v1.0/solutions/bookingBusinesses/{adTenantId}/appointments";
string accessToken = new JwtSecurityTokenHandler().WriteToken(AuthService.JwtToken);
HttpClient client = new();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false);
This gives me error 401 (Unauthorized). I may be wrong, but it seems to me that it is because I use the access token based on the user's authentication. I probably need to call the API not as the user, but as my app. Buy I don't know how to get an access token for the app.
The app is registered with Azure AD and has necessary API permissions.
Assume you have set up the app for MS Graph API correctly, and you have the below configurations:
ClientId
ClientSecret
TenantId
Scopes
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithClientSecret(ClientSecret)
.WithAuthority(new Uri("https://login.microsoftonline.com/" + Tenant))
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
var accessToken = result.AccessToken;
Note that I use scope https://graph.microsoft.com/.default for all the permissions you have assigned to the application. You could use more specific scopes.
References.
I have created an ASP.NET Framework application using Microsoft Identify Platform from the standard template and used ConfidentialClient to acquire an access token. I now want to use this access token to call the Azure DevOps REST API.
My scenario is:
Open the application and immediately get asked to log in
Acquire an access token from ConfidentialClient
Execute an API call to Azure DevOps (e.g. GET https://dev.azure.com/{organization}/_apis/projects)
I believe I have completed steps 1 and 2 (code below), but when I execute the API is doesn't return the results, merely a HTML page asking me to login
The access token is recovered from the following code:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var authCode = context.Code;
var tenantId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var authority = aadInstance + tenantId;
//string[] scopes = new string[] { "https://graph.microsoft.com/User.Read" };
string[] scopes = new string[] { "https://app.vssps.visualstudio.com/user_impersonation" };
//string[] scopes = new string[] { "https://graph.microsoft.com/User.Read", "https://app.vssps.visualstudio.com/user_impersonation" };
// Get the access token from the ConfidentialClientApplication)
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
var authResult = await app.AcquireTokenByAuthorizationCode(scopes, authCode).ExecuteAsync();
string accessToken = authResult.AccessToken;
Debug.WriteLine($"Access Token: {accessToken}");
//await GetProfileData(accessToken);
await GetProjectList(accessToken);
}
If I run this I get the access token but using this as the bearer token in my API call doesn't work. The method for calling the API is as follows:
private async Task GetProjectList(string accessToken)
{
// Get the Project List from the Azure DevOps API
var httpClient = new HttpClient();
var httpRequest = new HttpRequestMessage(HttpMethod.Get,
"https://dev.azure.com/gp-ementris/_apis/projects");
httpRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
"Bearer", accessToken);
var response = await httpClient.SendAsync(httpRequest);
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(await response.Content.ReadAsStringAsync());
}
}
Can someone help explain how I can get the API to work with the token?
Thanks
I am setting up a move to MSAL but the JWT token is missing the aud:
This is a WPF desktop app calling into a web api (ASP.Net)
.WithAuthority(AzureCloudInstance.AzurePublic, Tenant)
.WithDefaultRedirectUri()
.Build();
I have set the scope to the to match the scope set up in the webapi (app registration)
string[] scopes = new string[] { "https://*******.azurewebsites.net/access_as_user" };
then call
var accounts = await App.PublicClientApp.GetAccountsAsync();
AuthenticationResult authResult;
try
{
authResult = await App.PublicClientApp
.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (Exception)
{
authResult = await App.PublicClientApp
.AcquireTokenInteractive(scopes)
.ExecuteAsync();
}
The JWT token coming back always is missing the aud:
Then I call a simple test to see if the code will work.
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, "https://****.azurewebsites.net/api/Combo/getComboList");
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
I always get an error;
You do not have permission to view this directory or page.
Any pointer would be gratefully accepted.
Thanks
EDIT:
Here is a screen shot of the delegated permissions. On the Native client app under App Registration.
I'm trying to create an application that automatically sends data to my Smartcontract on my Azure Blockchain Workbench.
The problem is, I do not understand how to get the bearer token. There is an example online where I can yee how to call the API with GET and POST requests. But I have to submit a client app ID, a client secret, and a resource ID. Where do I get them from?
thanks a lot for your help and ideas !!
class Program
{
public static readonly string AUTHORITY = "https://login.microsoftonline.com/XXX";
public static readonly string WORKBENCH_API_URL = "https://XXX-api.azurewebsites.net";
public static readonly string RESOURCE = "XXX";
public static readonly string CLIENT_APP_Id = "XXX";
public static readonly string CLIENT_SECRET = "XXX";
static async Task Main(string[] args)
{
AuthenticationContext authenticationContext = new AuthenticationContext(AUTHORITY);
ClientCredential clientCredential = new ClientCredential(CLIENT_APP_Id, CLIENT_SECRET);
// Sample API Call
try
{
// Getting the token, it is recommended to call AcquireTokenAsync before every Workbench API call
// The library takes care of refreshing the token when it expires
var result = await authenticationContext.AcquireTokenAsync(RESOURCE, clientCredential).ConfigureAwait(false);
Console.WriteLine(result.AccessToken);
// Using token to call Workbench's API
//HttpClient client = new HttpClient();
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//client.DefaultRequestHeaders
// .Accept
// .Add(new MediaTypeWithQualityHeaderValue("application/json"));
//// Get Users
//var response = await client.GetAsync($"{WORKBENCH_API_URL}/api/v1/contracts");
//var users = await response.Content.ReadAsStringAsync();
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Bearer", result.AccessToken);
var content = await client.GetStringAsync($"{WORKBENCH_API_URL}/api/v1/contracts");
Console.WriteLine(content);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
According to my test, when we successfully created Azure blockchain workbench, we need to configure Azure AD when we access Azure blockchain workbench at the first time and we will create Azure AD application at the same time. The resource is the application ID or the app url of the Azure AD application. For more details, please refer to the document.
For example
Access Azure Blockchain workbench
Configure Azure AD
Create a Service Principal to Access Workbench API
cd; Invoke-WebRequest -Uri https://aka.ms/createWorkbenchServicePrincipalScript -OutFile createWorkbenchServicePrincipal.ps1
./createWorkbenchServicePrincipal.ps1 -TenantName <the tenant you use above> -WorkbenchAppId <the appid you copy> -MakeAdmin (optional)
Get token
Method: POST
URL: https://login.microsoftonline.com/<tenant id>/oauth2/token
Headers: Content-Type: application/x-www-form-urlencoded
Body:
grant_type: client_credentials
client_id: <sp client id>
client_secret:<sp client secret>
resource: <the app id>
Call rest api
URL: {WORKBENCH_API_URL}/api/v1/users
Headers: Authorization Bearer <access_token>
I am trying to use ADFS Authentication with OAuth to communicate between my webapp and webapi. I am using ADFS4 and have configured application group with Server application and Webapi accordingly. I am trying to receive the userdetails, particularly the username from the webapi controller. Is it possible to pass the username details within the access token passed to webapi. Here is what I did from the Webapp side:
In the webapp controller after adfs authentication,
authContext = new AuthenticationContext(Startup.authority, false);
ClientCredential credential = new ClientCredential(Startup.clientId, Startup.appKey);
string accessToken = null;
bool isAuthenticated = User.Identity.IsAuthenticated; //return true
string username = User.Identity.Name; // returns username
string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value; // returns username
HttpClient httpClient = new HttpClient();
try
{
result = authContext.AcquireTokenAsync(Startup.apiResourceId, credential).Result;
accessToken = result.AccessToken;
}
catch (AdalException ex)
{
}
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = httpClient.GetAsync(Startup.apiResourceId + "/api/ConfApi").Result;
From the Webapi end, in Startup.Auth.cs, I have added these code
public void ConfigureAuth(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap.Clear();
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
TokenValidationParameters = new TokenValidationParameters() {
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
}
});
}
However, within the ConfApi controller, I cannot find any claims with user details.
What can I do to receive user details in the Webapi controller?
Thanks for any help.
Are you actually receiving the claims?
Did you configure claims rules for the web API on the ADFS side?
What did you use for Name - Given-Name, Display-Name etc?
Use something like Fiddler to monitor the traffic. After the OIDC authentication, you should see access tokens, id tokens etc.
Take the token and copy into jwt.io.
This will show you what you are actually receiving.
However, the OWIN classes translate the simple OAuth attributes e.g. "aud" into the claim type URI e.g. http://claims/this-claim so breakpoint and see what is in the claims collection and what type has been assigned to each.
The answer to this is the same answer to the question: MSIS9649: Received invalid OAuth request. The 'assertion' parameter value is not a valid access token
You have to use authorization code flow (instead of client credentials grant flow) to get the server app (web app in this case) to talk to the web API with the user's context. Authorization code flow will pass the claims in the JWT Token. Just make sure you pass thru any claims you need for the web API in the web API's RPT claim issuance transform rules.
Vittorio has a nice post on authorization code flow, although it talks about azure.
In order to use authorization code flow, you need to handle the AuthorizationCodeReceived Event via Notifications on the OpenIdConnectAuthenticationOptions from Startup.ConfigureAuth(IAppBuilder app)
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
...
Notifications = new OpenIdConnectAuthenticationNotifications {
AuthorizationCodeReceived = async code => {
ClientCredential credential = new ClientCredential(Startup.clientId, Startup.appKey);
AuthenticationContext authContext = new AuthenticationContext(Startup.authority, false);
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
code.Code,
new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),
credential,
Startup.apiResourceId);
}
}
When you are ready to make the call you acquire your token silently.
var authContext = new AuthenticationContext(Startup.authority, false);
var credential = new ClientCredential(Startup.clientId, Startup.appKey);
var claim = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userId = new UserIdentifier(claim, UserIdentifierType.UniqueId);
result = await authContext.AcquireTokenSilentAsync(
Startup.apiResourceId,
credential,
userId);
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer",
result.AccessToken);