Reputation: 645
I have 2 projects, one is for API and 2nd is for to view and get data by using 1st project's APIs. For both I have implemented Azure AD authentication but my issue is when try to call API from 2nd project back-end(C#) with Bearer token but in response i am getting below error.
Note: I have use [Authorize] Filter in each and every class and the method in both project.
Error
AuthenticationFailed: IDX10501: Signature validation failed. Unable to match 'kid': 'SSQdhI1cKvhQEDSJxE2gGYs40Q0', token: '{"alg":"RS256","typ":"JWT","kid":"SSQdhI1cKvhQEDSJxE2gGYs40Q0"}.{"aud":"1e615ddb-ad4d-4e65-98de-c6f5db1ae08a","iss":"https://login.microsoftonline.com/5c58f0d9-2f98-4eb0-91f2-ec6afd4242f8/v2.0","iat":1518520450,"nbf":1518520450,"exp":1518524350,"aio":"Y2NgYHiknfZAIvPElucJpgeZzRa5AAA=","azp":"1e615ddb-ad4d-4e65-98de-c6f5db1ae08a","azpacr":"1","e_exp":262800,"oid":"159f0ec6-c5b9-4bfc-88d0-77924bd40b3f","sub":"159f0ec6-c5b9-4bfc-88d0-77924bd40b3f","tid":"5c58f0d9-2f98-4eb0-91f2-ec6afd4242f8","uti":"t8CU6YtsHE-5M9TbQm4aAA","ver":"2.0"}'.
Start class ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
.AddCookie();
// Add framework services.
services.AddMvc();
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
options.CookieHttpOnly = true;
});
}
Start class Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSession();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Class for to get Bearer Token
public class ServicePrincipal
{
static string authority = "https://login.microsoftonline.com/{TenantID}/{Policy}/v2.0/";
static string clientId = "XXX";
static string clientSecret = "XXX";
static string resource = "XXX";
static public async Task<Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult> GetS2SAccessTokenForProdMSAAsync()
{
return await GetS2SAccessToken(authority, resource, clientId, clientSecret);
}
static async Task<Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult authenticationResult = await context.AcquireTokenAsync(resource,clientCredential);
return authenticationResult;
}
}
Controller method from where I am trying to calling 1st project's API
public async Task<IActionResult> GetCustomerGroupAsync()
{
try
{
var token = await ServicePrincipal.GetS2SAccessTokenForProdMSAAsync();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
HttpResponseMessage response = await _client.GetAsync("http://localhost:49942/api/customermodule/v0.3/customergroup");
response.EnsureSuccessStatusCode();
string receiveStream = await response.Content.ReadAsStringAsync();
return null;
}
catch (Exception e)
{
return Json(new { Error = e.Message });
}
}
Please let me know if I am missing anything or am i doing wrong?, thank you.
Upvotes: 2
Views: 1785
Reputation: 18465
Based on your GetCustomerGroupAsync
action, you are using the client credentials flow in your website backend to access the secured resource (Web API) with Azure AD B2C. As Azure Active Directory B2C: Types of applications mentions under the Current limitations section as follows:
Daemons/server-side apps
Apps that contain long-running processes or that operate without the presence of a user also need a way to access secured resources such as web APIs. These apps can authenticate and get tokens by using the app's identity (rather than a user's delegated identity) and by using the OAuth 2.0 client credentials flow.
This flow is not currently supported by Azure AD B2C. These apps can get tokens only after an interactive user flow has occurred.
Moreover, you could follow the git samples below to implement your requirement:
An ASP.NET Core 2.0 web API with Azure AD B2C
An ASP.NET Core web app with Azure AD B2C
Upvotes: 2
Reputation: 1865
Because you need to retrive access token, it must be a call to OAuth endpoint. Try with the below one to get Azure AD access token
https://login.microsoftonline.com/{TenantID}/oauth2/token
More over, just need to make sure the authorization header is constructed in valid format: Bearer
Upvotes: 0