Azure AD - Expose an API to consume it with application permissions - api

I'm developing a custom API and a windows service. I want to use Azure AD for authentication and authorization between the api and the windows service.
My question ist: How can I expose the API, so that I can add the api permissions to the win service with the type "application permissions"?
I can only select "delegated permissions" when I want to add the api permissions to the win service. But I need "application persmissions" because the win service runs without an user.
Thank you in forward!
Best regards
Matthias
Ok. Now I know how to set up the manifest in the app registration. I also get a bearor token and in the bearor token I can see (if I bas64-decode it) the Client ID of the Web API and also the App roles: "roles":["User.Sync2"]
So I think that the token is correct.
In the second step I call the Web API (https://localhost:44358/api/AzureADB2C/Ping) with authentication "Bearor" and the token. But then I receive a 401. (I have not registered any platform in the app registration for the Web API and therefore also no redirect URI. But I think I don't need it?)
Here's the Startup.cs of my Web API project (It's standard generated with Visual Studio):
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace AzureADB2CConnect.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
and here is my API Controller:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AzureADB2CConnect.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AzureADB2CController : ControllerBase
{
[HttpGet("Ping")]
[Authorize(Roles = "User.Sync2")]
//[Authorize]
public async void GetPing()
{
//foreach(Claim claim in ClaimsPrincipal.Current.Claims)
//{
//}
}
}
}
If I remove the "Authorized"-Tags I can call the API. And it doesn't matter If I use only Authorize or Authorize(Roles = "User.Sync2") I always receive a 401.
Where is the error/bug?
Thank you in forward!
Here is the decoded bearor token:
That's how I call the GET-Method to get the token:

you need to add app roles. Please follow How to: Add app roles in your application and receive them in the token. App role allowedMemberTypes should include Application.

I can reproduce your problem. You entered the wrong parameters when verifying the token. You need to change Authorized to Authorization and Bearor to Bearer.

Related

Blazor server with web api controller authenticate issue

I have a Blazor server app that I want to add a web api controller to that can be accessed from Postman and eventually other apps. The Blazor app needs authentication, but not the web api. I tried adding AllowAnonymous, but I am getting an authentication error calling it from Postman:
HTTP Error 401.2 - Unauthorized
You are not authorized to view this page due to invalid authentication headers.
I suspect our security proxy is adding the headers:
Is it possible to host an unsecured (AllowAnonymous) web api inside an authenticated Blazor Server app?
Maybe I just need to craft my api call a certain way?
Controller:
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class ProfileController : ControllerBase
{
[HttpGet("{year}", Name = "GetProfileResults")]
public async Task<IActionResult> GetProfileResults(int year)
{
var profileResults = repo.GetResults(year);
return Ok(profileResults);
}
}
You have to add another http client with no tokens attached.
Program.cs
builder.Services.AddHttpClient(
name: "Anon.ServerAPI",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
RazorPage.razor.cs
[Inject]
public IHttpClientFactory HttpClientFactory { get; set; }
protected override async Task OnInitializedAsync()
{
http = HttpClientFactory.CreateClient("Anon.ServerAPI");
videos = await http.GetFromJsonAsync<VideoDto[]>("api/YoutubeVideos");
}
The key point to host a public API in a Blasor Server app is to ensure the API routing takes precedence over others.
In Program.cs (or Startup.cs):
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); // the order is important, this ensures API takes precedence.
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Alternatively for endpoint routing:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
app.MapControllers(); // the order is important, this ensures API takes precedence.
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
});
app.Run();
Next, the controller. In your example the code is completely correct. It must use [AllowAnonymous] at the controller level or at specific actions as usual.
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class ProfileController : ControllerBase
{
[HttpGet("{year}", Name = "GetProfileResults")]
public async Task<IActionResult> GetProfileResults(int year)
{
var profileResults = repo.GetResults(year);
return Ok(profileResults);
}
}
That should be enough to route the call to API before Blazor takes over the security.
Last but not the least is the default exception configuration handling code added to Blazor projects by default:
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
Please be aware that when this code is used any unhandled exceptions during an API call will be caught by the error handler which doesn't respect API [AllowAnonymous] settings and may trigger the authentication challenge configured for Blazor.

Http Post in Orchard Core asp net core Web App returns bad request

I'm using Orchard core in asp net core web app project. I have a controller with two simple get and post Apis. As I'm using OrchardCore the Startup.cs file has different config and I dont use services.AddControllers() in configureServices.
Every thing is fine untill I'm using HttpGet. But when I want to have an Api with HttpPost postMan says badRequest. So I Added services.AddControllers() in Startup.cs and the post Api was fine in post Man but the orchard project says I have multipe Endpoints.
I used services.AddMvc().AddNewtonsoftJson(), and every thing was fine but the admin page didn't load and had error as below:
InvalidOperationException: The view 'Index' was not found. The
following locations were searched:
/Areas/OrchardCore.AdminDashboard/Views/Dashboard/Index.cshtml
/Areas/OrchardCore.AdminDashboard/Views/Shared/Index.cshtml
/Views/Shared/Index.cshtml /Pages/Shared/Index.cshtml
I wold appreciate it if you can help me how to call Post Api.
here is my code:
[HttpPost("post")]
public Task<string> post()
{
return Task.FromResult("hiPost");
}
[HttpGet("get")]
public Task<string> get()
{
return Task.FromResult("hiGet");
}
and this is my startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//services.AddControllers();
services.AddOrchardCms();
services.AddMediatR(typeof(SelectedWebSiteBlogQueryHandler).Assembly);
services.AddAutoMapper(typeof(Startup));
services.AddCors();
services.AddMvc().AddNewtonsoftJson();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(o => o.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseOrchardCore();
}
}
You are probably missing an IgnoreAntiForgeryToken attribute on your controller.
AntiForgery is enabled by default by OrchardCore
For an ApiController in OrchardCore I would expect to see the controller decorated as follows.
[ApiController]
[Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous]
However this depends if you are using the OpenId module to authenticate with, or simply need to post to a normal controller, without an AuthenticationScheme
Depending on what you are actually posting from in real life, it may be better to supply an anti forgery token as part of your post.

Secure API .net core 3.1 (upgraded api from 2.2) with identityserver no longer works, is my middleware correct?

I have an API (.net core 2.2) protected and working fine with Identity server.
I need to upgrade this API to .net core 3.1. So I started fresh with a core 3.1 API project and added in the controllers, dbContext and the middleware... I have tried to match the middleware as close to the 2.2 as possible (which may be my problem) but I am receiving a 401 unauthorised on methods decorated with [Authorize] (without even declaring any roles or policies, just a simple Authorize). If I take off the [Authorize] this works fine... so here lies the problem.
I have tried to find examples of upgrading the API from 2.2 to 3.1 from a middleware IdentityServer point of view, but only able to find examples of upgrading IdentityServer itself (not a protected API) between these versions.
I have also analysed the User claims in the API method, which all look fine. This works fine on 2.2, so I think this must be to do with the middleware, but I'm not sure... can someone point me in the right direction please? Here is my startup file:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using HMS.API.Services;
using HMS.Global.Repository;
namespace HMS.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddControllers();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "https://????.???????.com"; // Removed for this post
options.ApiName = "hmsapi";
});
services.AddDbContext<DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("HMSConnection")));
// Create policy
services.AddAuthorization(authorisationOptions =>
{
authorisationOptions
.AddPolicy(
"HMSADMINPOLICY",
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim("role", "HMSADMIN");
});
authorisationOptions
.AddPolicy(
"HMSSTANDARDPOLICY",
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim("role", "HMSSTANDARD", "HMSADMIN");
});
});
services.AddSingleton<IUserService, UserService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
I would use Fiddler to debug the traffic to/from my client application and especially look at the claims that are actually returned from IdentityServer.
If you are getting 401 not authorized when you just use [Authorize], then you are not really logged in. All users who are logged in should pass [Authorize].
If you are also upgrading to IdentityServer4 v4.0x , then you need to add your ApiScopes as well (New feature in v4.x)

ASP .Net Core Google Authentication

I have a problem with google authentication on my .net core web api application.
My use case is simple, get bearer token from google put token in authorization header as "Bearer {token}" and call my web api.
But I cannot make it work. After I get token from google on following url:
https://accounts.google.com/o/oauth2/v2/auth?scope=email%20openid&include_granted_scopes=true&state=some_test_state&redirect_uri=http%3A%2F%2Flocalhost%3A53512&response_type=token&client_id={someClientID}
I will make call to my api with header:
Authorization: Bearer {TokenValue}
But every time I'm getting 401 Unauthorized.
This is my Startup class:
public static IConfigurationRoot Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Pull in any SDK configuration from Configuration object
services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
// Add S3 to the ASP.NET Core dependency injection framework.
services.AddAWSService<Amazon.S3.IAmazonS3>();
IocConfig.Configure(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLambdaLogger(Configuration.GetLambdaLoggerOptions());
var googleOptions = new GoogleOptions
{
AuthenticationScheme = "Google",
ClientId = "clientid",
ClientSecret = "cs",
SignInScheme = "Google"
};
app.UseGoogleAuthentication(googleOptions);
app.UseDeveloperExceptionPage();
app.UseMvc();
}
It's because your authentication scheme is "Google", but if you want to use bearer token you need to add it to your startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// here's your options
})
And use this authentication scheme instead of "Google"

IdentityServer 4: No storage mechanism for grants specified - use AddInMemoryStores

I am using Identity Server 4 , ASP.NET Core and trying to replace the IdentityServer developer in Production environment. But getting the following error:
No storage mechanism for grants specified. Use the 'AddInMemoryStores' extension method to register a development version.
So, I tried to implement the services as mentioned in this answer:
IProfileService
IResourceOwnerPasswordValidator
This is my ConfigureServices Method in Startup class:
services.AddMvc();
var identityBuilder = services.AddIdentityServer();
identityBuilder.AddInMemoryScopes(identitySrvConfig.GetScopes());
identityBuilder.AddInMemoryClients(identitySrvConfig.GetClients());
identityBuilder.AddProfileService<ProfileService>();
identityBuilder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
Taking into consideration that in my case the interface signature is different:
public class ResourceOwnerPasswordValidator : IdentityServer4.Validation.IResourceOwnerPasswordValidator
{
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
throw new NotImplementedException();
}
}
But I am still getting the same error, what is the problem?
If you are applying custom Identity i.e
services.AddIdentity<AppUser, UserRole>(options => { options.User.RequireUniqueEmail = true; }).AddEntityFrameworkStores<AbcDbContext>();
then in
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
Comment
app.UseIdentityServer();
because we are using custom identity, not default
They were/are reworking those APIs. You should use AddInMemoryPersistedGrants