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>
Related
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 was needing to retrieve a entity record from dynamics finance and operations and I did it in Postman but now I need to the same with code. I am using asp.net core and when I try to retrieve the entity the response gives me HTML. I figure out is because the authorization needs to be the same as the postman that is a post operation with grant_type, clientId, client secret and Resource.
How can I do post operation in C# with grant_type, clientId, client secret and Resource parameters to get the access token?
You have 2 options. In either of the cases, you might want to give a quick read to Microsoft identity platform and the OAuth 2.0 client credentials flow.
Using MSAL client library (note ADAL is deprecated now). Below is a quick code snippet. Full example can be found here. Also, read more here.
var app = ConfidentialClientApplicationBuilder.Create("<client id>")
.WithClientSecret("<client secret>")
.WithAuthority(new Uri("<authority>")) // authority = https://login.microsoftonline.com/{tenant}
.Build();
// With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal or by PowerShell), and then granted by a tenant administrator
var scopes = new string[] { "<scope>" };
var result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
var accessToken = result.AccessToken;
// call api with http authorization header
// Authorization: Bearer <Access Token>
Using REST API directly from C# code using httpClient.
var client = new HttpClient(); // just for example I am creating the client inline
var result = await client.PostAsync(
"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",
new FormUrlEncodedContent(new[]
{
{"client_id", "<client id>"},
{"grant_type", "client_credentials"},
{"client_secret", "<client secret>"},
{"scope", "<scope>"},
}));
var responseBody = await result.Content.ReadAsStringAsync();
// response would be a JSON, just extract token from it
var accessToken = (string)JToken.Parse(responseBody)["access_token"];
// call api with http authorization header
// Authorization: Bearer <Access Token>
I am trying to get information about users from Microsoft Graph via
https://graph.microsoft.com/v1.0/users.
It's returning a 401 - Unauthorized:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure. Invalid audience.",
"innerError": {
"request-id": "3157d513-6f31-4d2d-a3d7-a97eed7207ba",
"date": "2019-12-11T05:39:02"
}
}
}
My code:
AuthenticationContext authContext =
new AuthenticationContext(string.Format(CultureInfo.InvariantCulture,
"https://login.microsoftonline.com/{0}", "my-domain name"));
ClientCredential clientCred =
new ClientCredential("Client-id", "Client-Secret-id");
AuthenticationResult authenticationResult = authContext
.AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/me/";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token);
var response = await client.GetAsync(uri);
Where I did go wrong? Why I am not getting a proper access token? Could anyone please help me to use the MS Graph?
You use the wrong resource, you need to get the token for Microsoft Graph instead of AAD Graph,
it should be https://graph.microsoft.com, not https://graph.windows.net.
AuthenticationResult authenticationResult = authContext.AcquireTokenAsync("https://graph.microsoft.com",
clientCred).Result;
Update:
Make sure you grant the User.Read.All Application permission.
Then try the code as below, it works on my side.
using System;
using System.Net.Http;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string _authString = "https://login.microsoftonline.com/xxxxxx.onmicrosoft.com";
string _clientId = "<client-id>";
string _clientSecret = "<client-secret>";
AuthenticationContext authenticationContext = new AuthenticationContext(_authString, false);
ClientCredential clientCred = new ClientCredential(_clientId, _clientSecret);
AuthenticationResult authenticationResult;
authenticationResult = authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCred).GetAwaiter().GetResult();
Console.WriteLine(authenticationResult.AccessToken);
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/users";
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Accept.Clear();
//GET Method
HttpResponseMessage response = client.GetAsync(uri).GetAwaiter().GetResult();
Console.WriteLine(response.Content.ReadAsStringAsync().Result.ToString());
}
}
}
I think if you're calling Microsoft Graph the resource needs to be https://graph.microsoft.com instead of AAD Graph (graph.windows.net). Can you try changing that in your AcquireTokenAsync call?
There are two issues :
Wrong resource , the resource should be https://graph.microsoft.com . And confirm that you have grant correct Microsoft Graph's permissions in Azure AD portal
You are using client credential flow as using AcquireTokenAsync(String, ClientCredential) method without user , so https://graph.microsoft.com/v1.0/me/ won't work since there is no user in it . Use GET /users/{id | userPrincipalName} instead . Also , you should grant Application Permission in azure portal since you are using M2M flow .
Permissions (from least to most privileged) :
Application :User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All
Make sure the account you're using while making the Graph API calls has the Required Permissions. As you're invoking a GET call,
Below permissions should be set up.
More about permissions here: https://learn.microsoft.com/en-us/graph/permissions-reference
The Error posted clearly states that the account you're using to make calls to GRAPH API is unauthorized. Have the permissions set right and the access token will be generated and will be authenticated against your application.
EDIT: Try the below code to get a valid access token.
static string AppID = "<Your Application ID>";
static string APPKey = "<Your Application Key>";
static string tenantId = "<Your ORG Tenant ID>";
static string RedirectURI = "<Your Application's custom Redirect URI>";
static string GraphApi = "https://graph.microsoft.com/v1.0/"
public static IAuthenticationProvider CreateAuthorizationProvider()
{
var authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var cca = ConfidentialClientApplicationBuilder.Create(AppID)
.WithAuthority(authority)
.WithRedirectUri(RedirectURI)
.WithClientSecret(APPKey)
.Build();
return new MsalAuthenticationProvider(cca, scopes.ToArray());
}
public static HttpClient GetAuthenticatedHTTPClient()
{
var authenticationProvider = CreateAuthorizationProvider();
_httpClient = new HttpClient(new AuthHandler(authenticationProvider, new HttpClientHandler()));
return _httpClient;
}
private static async Task<User> GetADUserInfo(HttpClient client,string email)
{
User user = new User();
client = GetAuthenticatedHTTPClient();
client.BaseAddress = new Uri(GraphApi);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
WriteToConsole("Call Graph API :: retrieving AD Info for the employee ::" + email);
using (client)
{
try
{
HttpResponseMessage res = await client.GetAsync("users/" + email);
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
user = await res.Content.ReadAsAsync<User>();
WriteToConsole("Call Graph API :: Call Success for employee ::" + email);
}
}
catch (Exception ex)
{
LogError(ex, "Error in Getting AD User info via Graph API");
return null;
}
return user;
}
}
The Above code uses MSALAuthentication, Use the code below :
public class MsalAuthenticationProvider : IAuthenticationProvider
{
private IConfidentialClientApplication _clientApplication;
private string[] _scopes;
public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
{
_clientApplication = clientApplication;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = null;
authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
return authResult.AccessToken;
}
}
AuthHandler Class :
public class AuthHandler : DelegatingHandler
{
private IAuthenticationProvider _authenticationProvider;
public AuthHandler(IAuthenticationProvider authenticationProvider, HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler;
_authenticationProvider = authenticationProvider;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await _authenticationProvider.AuthenticateRequestAsync(request);
return await base.SendAsync(request, cancellationToken);
}
}
You have a few issues going on:
You should be requesting a token for https://graph.microsoft.com, not https://graph.windows.net. The graph.windows.net is the older AAD Graph, not the newer Microsoft Graph:
AuthenticationResult authenticationResult = authContext
.AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
You cannot use /me with the Client Credentials grant. Graph translates /me into /users/{currently authenticated user id}. Since you're not authenticating a user, the "currently authenticated user id" is null:
var uri = "https://graph.microsoft.com/v1.0/users/user#domain.onmicrosoft.com";
You are setting the Authorization header's value but not the scheme. You need to set both:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
It isn't clear from your question which scopes you've requested or if you've received Admin Consent. You need to make sure you've requested the Application scope User.Read.All and received Admin Consent from a tenant administrator.
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 .
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);