Handle AAD login with site forwarded from Azure API Management - azure-active-directory

I have a Service Fabric cluster that is running a service (Stateless ASP.Net Core) that can't be exposed to the Internet. Requests to the service are routed through API Management.
Incoming request ==> https://blah.trafficmanager.net/routeName
forwarded ==> https://10.0.4.6:[port]/routeName
This works fine with no authentication. I am hooking up AAD auth as we need to lock this down to a client registration (due to timeboxed elevation request requirements). I have set up the service to use AAD auth, and when running in my local development Service Fabric cluster, this works fine (i.e. prompted to log in, routes are gated based on roles) using these parameters:
"AzureAd:ClientId": "[client id]",
"AzureAd:Domain": "[tenant].onmicrosoft.com",
"AzureAd:Instance": "https://login.microsoftonline.com/",
"AzureAd:SignedOutCallbackPath": "/signout-callback-oidc",
"AzureAd:CallbackPath": "/signin-oidc",
"AzureAd:TenantId": "[tenant id]",
When I deploy this and try to hit it via https://blah.trafficmanager.net/routeName, the redirect https://10.0.4.6:44321/signin-oidc, and I end up with:
Here is my startup.cs code:
/// <summary>
/// Configure services
/// </summary>
/// <param name="services">Service collection</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
// Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});
// Sign-in users with the Microsoft identity platform
services.AddMicrosoftWebAppAuthentication(this.Configuration);
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownProxies.Add(IPAddress.Parse("10.0.4.6"));
});
// services.AddControllers();
services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
}
/// <summary>
/// Configure app
/// </summary>
/// <param name="app">App builder</param>
/// <param name="env">Environment</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseForwardedHeaders();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I am wondering if there is something I can do in order to be able to have this service on the cluster node be able to authenticate requests that are coming in through APIM.
Thanks!

I was able to override the reply url with this:
services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
var redirectToIdpHandler = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = async context =>
{
// Call what Microsoft.Identity.Web is doing
await redirectToIdpHandler(context);
// Override the redirect URI so that auth works behind APIM
context.ProtocolMessage.RedirectUri = this.Configuration["AzureAd:RedirectUri"];
};
});
I then had auth problems as it would loop back many times and finally give a sign in error. I found this was because my reply url was to the main site instead of signin-oidc. When I added signin-oidc with the APIM as reply url, APIM failed as there was no route with this name. I created a new API that forwarded signin-oidc to the same route on the back end.
Now the reply back from AAD hits my APIM at signin-oidc and is forwarded to the same on the back. This allows auth to complete.

Related

Dreaded AADSTS50011 reply url error for web app login using Azure AD - runs fine on localhost

Had a C# LoB test app authenticating and authorising nicely using Azure AD with a reply url of /signing-oidc
Suddenly it's stopped validating, and the dreaded AADSTS50011 url error comes up.
I can't figure out why this has changed?
Using Microsoft.Identity.Web with the following startup.cs ConfigureServices and Configure methods:
public void ConfigureServices(IServiceCollection services) {
services.AddMicrosoftIdentityWebAppAuthentication(Configuration);
//.EnableTokenAcquisitionToCallDownstreamApi();
services.AddControllersWithViews(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages().AddMvcOptions(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
services.AddServerSideBlazor(o => o.DetailedErrors = true);
services.AddScoped<IGpsDataService, GpsDataService>();
services.AddTelerikBlazor();
}
// 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();
}
else {
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();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
Pretty much boilerplate.
Can anyone point me to the correct callback url? I have it as /signin-oidc and that's configured in the appsettings.json file and on the Azure AD app authentication settings.
Stumped. Runs fine on localhost, but not in Azure 🤷‍♂️
To deal with this kind of problem, you can try a general solution:
When you visit the application url , you will be redirected to the login page. Decode the authorization request url, you will find redirect_url, copy the value of redirect_url and paste it into the azure portal, and try again.
For the redirect URL, it should start with https, if you need to start with http, you must configure it as http://localhost.

SignalR Hub not receiving user from client WPF (.netcore 3.1)

I have a WPF client which connects successfully to a Hub, but I cannot pass the user of the client to the Hub.
My connection.User?.Identity?.Name in my class implementing from IUserIdProvider returns null.
For my WPF client I use this to connect against the Hub:
_connection = new HubConnectionBuilder()
.WithUrl(viewModel.Endpoint, opts =>
{
opts.Credentials = new NetworkCredential("user", "password", "domain");
opts.UseDefaultCredentials = true;
})
.Build();
I have then the following provider registered as singleton:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
As I mentioned above, the connection.User?.Identity?.Name; is returning null.
I don't know what else I can do to pass the user name from my client (WPF) to my Hub.
By the way, my Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddLogging();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddSignalR(hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
});
services.AddScoped<IBuildsService, BuildsService>();
}
// 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.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<SyncCodeHub>("/signalr");
});
}
Any help would be much appreciated.
EDIT:
I update the code with:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
But the problem continues, the identity user (IUserIdProvider) is returning null when called from the WPF client. I'm running the API locally with IISExpress.
EDIT:
From Microsoft docs:
Windows Authentication is only supported by the browser client when using Microsoft Internet Explorer or Microsoft Edge.
So I'm wondering if this is even possible with an Desktop as a client. I assume it should work, so I'm wondering if I'm still missing a point or if this is a bug related to the Version of SignalR I#m using (3.1.3)
You need to configure your ASP.NET Core app to use Windows authentication by calling AddAuthentication in the ConfigureServices method of the Startup class:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
You should also edit your launchSettings.json file according to the docs:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": false,
"iisExpress": {
"applicationUrl": "http://localhost:52171/",
"sslPort": 44308
}
}

.Net Core 2 WebApi to SQL Server using Active Directory

I am working on a project where I am using .net core 2.
I have a Client app, which calls an Web API, with the API doing communication with the database (SQL Server 2017). As this is an internal (Intranet) app only, we are using AD (Active Directory) as our method of authentication an security of the app.
When working locally the App and DB uses the users details (i.e. name and login details) correctly and are passed through the app and used by the API to talk to the database. The database call then using suser_name to automatically record the users name in a table. We need to know the individual users actions, not use a SQL Server Login which used by the app as a whole.
When the application is moved to the test environment, a web server (IIS8.5) which holds the client and the API in different virtual directories and the database which is on a different server. Both servers are in the same domain (NL-TEST).
When updating the connection string from:
Server=LT017180;Database=Payments; Integrated Security=SSPI;
to Server=testserver;Database=Payments; Integrated Security=SSPI;
it will not connect to the database.
I have confirmed that the user (NL-TEST\Joe_Bloggs for example) is setup in the security section of the database server and has access to the database and is in the NL-TEST domain as a user (I have set it to db_owner, just to try to get it to work).
I have confirmed that the API does have the users credentials. How do I get the credentials to the database so that it logs in as the user and accesses the database?
Example of my code:
Client
Startup
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
private IHostingEnvironment Environment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<Entities.APIs>(Configuration.GetSection("APIURLs"));
services.AddPolicies(Configuration.GetSection("Policies").Get<Policies>());
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromDays(1);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
API
public IDbConnection Connection => new SqlConnection(_connectionString);
[HttpGet("payment/employee/{id}")]
public IEnumerable<PaymentDTO> GetByEmployeeId(Int64 id)
{
using (IDbConnection dbConnection = Connection)
{
var parameters = new DynamicParameters();
parameters.Add("EmployeeNo", id, DbType.Int64);
dbConnection.Open();
var payments = dbConnection.Query<PaymentDTO>("api.GetPaymentsForEmployeeNo", parameters, commandType: CommandType.StoredProcedure);
dbConnection.Close();
return payments;
}
}
Ajax call to API
var onAjaxSuccess = function (data) {
// do something
};
$.ajax({
headers: {
'Accept': 'application/json'
},
contentType: 'application/json',
url: href,
type: "POST",
cache: false,
error: function (result) {
// record an error
},
success: onAjaxSuccess,
xhrFields: {
withCredentials: true
},
processData: false
});
Controller to API call:
private static readonly HttpClient Client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true});
private static async Task<PaymentDTO> GetPaymentsDetails(Int64 paymentId)
{
var url = new Uri(ApiUrls.PaymentsAPI + "/payments/payment/" + paymentId);
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await Client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var jsonString = await response.Content.ReadAsStringAsync();
var payments = JsonConvert.DeserializeObject<List<PaymentDTO>>(jsonString);
var payment = payments[0];
return payment;
}
return new PaymentDTO();
}
Am I missing a setting or wrapper?
Prior to .NET Core, all you'd have to do is disable Anonymous Authentication, enable Windows Authentication, and enable Impersonation (either via the web.config of the project or if deploying to IIS you can also do it in the Authentication settings of the site.)
.NET Core scrapped Impersonation so this is no longer possible. Without being able to use Impersonation, when deployed to IIS, the application (and connections to the database) are executing as the AppPool user of that site (not the actual user browsing the site, even when Windows Authentication is enabled).
They do offer a somewhat legacy solution for how to impersonate the Windows Authenticated user but it isn't really recommended, especially for heavy units of work. Read on the Impersonation section at the bottom: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio
Alternative solutions to this problem are:
Develop your own login screen to get a reference to the users credentials and use those to make your database connections (obviously this should be implemented in a secure way)
Implement OAuth authentication

How to force authentication to run so that the principal is available for other ASP.NET Core middleware?

I am trying to add a middleware to implement throttling in my Web API based on client id. This Web API is protected by Identity Server 4 and the JWT authentication handler.
The problem is that Context.User.Claims is always empty when my middleware runs.
I understand that the Jwt handler only gets called when the request hits the Authorize attribute.
Thus, my question is, how can I "force" the Jwt handler to run sooner in the pipeline so that my middleware gets the call after the token is validated and the client_id claim is available in the context principal?
Thanks for any help you can give me.
The code that setups the Web API is as follows:
public void ConfigureServices(IServiceCollection services)
{
// Validation
SmartGuard.NotNull(() => services, services);
// Log
this.Logger.LogTrace("Application services configuration starting.");
// Configuration
services
.AddOptions()
.Configure<ServiceConfiguration>(this.Configuration.GetSection(nameof(ServiceConfiguration)))
.Configure<TelemetryConfiguration>(this.Configuration.GetSection(nameof(TelemetryConfiguration)))
.Configure<TableStorageServiceConfiguration>(this.Configuration.GetSection(nameof(TableStorageServiceConfiguration)))
.UseConfigurationSecrets();
ServiceConfiguration serviceConfiguration = services.ResolveConfiguration<ServiceConfiguration>();
// Telemetry (Application Insights)
services.AddTelemetryForApplicationInsights();
// Memory cache
services.AddDistributedMemoryCache();
// MVC
services.AddMvc();
// Identity
services
.AddAuthorization(
(options) =>
{
options.AddPolicy(
Constants.Policies.Settings,
(policy) =>
{
policy.RequireClaim(Constants.ClaimTypes.Scope, Scopes.Settings);
});
});
// NOTE:
// We are using the JWT Bearer handler here instead of the IdentityServer handler
// because version 2.3.0 does not handle bearer challenges correctly.
// For more info: https://github.com/IdentityServer/IdentityServer4/issues/2047
// This is supposed to be fixed in version 2.4.0.
services
.AddAuthentication(Constants.AuthenticationSchemes.Bearer)
.AddJwtBearer(
(options) =>
{
options.Authority = serviceConfiguration.IdentityServerBaseUri;
options.Audience = Constants.ApiName;
options.RequireHttpsMetadata = false;
options.IncludeErrorDetails = true;
options.RefreshOnIssuerKeyNotFound = true;
options.SaveToken = true;
options.Events = new JwtBearerEvents()
{
OnChallenge = HandleChallenge
};
});
// Web API Versioning
services.AddApiVersioning(
(options) =>
{
options.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(ApiVersions.DefaultVersion.Major, ApiVersions.DefaultVersion.Minor);
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
// Setup Throttling
services
.AddThrottling()
.AddClientRateHandler(this.Configuration.GetSection(nameof(ClientRateThrottlingOptions)));
// Routes analyzer
// Creates the /routes route that lists all the routes configured
services.AddRouteAnalyzerInDevelopment(this.CurrentEnvironment);
// Add the managers
services.AddManagers();
// Background services
services.AddBackgroundService<StorageSetupService>();
// Log
this.Logger.LogTrace("Application services configuration completed.");
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Validation
SmartGuard.NotNull(() => app, app);
SmartGuard.NotNull(() => env, env);
// Log
this.Logger.LogTrace("Application configuration starting.");
// Error handling (Telemetry)
app.UseTelemetryExceptionHandler();
// Authentication
app.UseAuthentication();
// Register the throttling middleware
app.UseThrottling();
// MVC
app.UseMvc(
(routes) =>
{
// Routes analyzer
routes.MapRouteAnalyzerInDevelopment(env);
});
// Log
this.Logger.LogTrace("Application configuration completed.");
}
The relevant middleware code is as follows:
internal class ClientRateMiddleware : IClientRateThrottlingMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
(...)
Claim claim = context.User.FindFirst("client_id");
// Claim is always null here because the Jwt handler has not run
(...)
}
}
OK, so I think I have kind of cracked this one. I think #Hugo Quintela Ribeiro is right about the authorization only occurring when the [Authorize] filter is hit, or when a controller that does not [Allow Anonymous] is hit in the case that authorization is set for the whole app. This of course happens at the controllers, and not in the middleware.
It turns out you can actually force authentication to occur in the middleware. I tried a couple of things like the following with no success.
await context.AuthenticateAsync();
await context.AuthenticateAsync("Custom"); //name of my jwt auth
In the end, I had to inject IAuthorizationPolicyProvider and IPolicyEvaluator to get the default policy and authenticate it.
using cpDataORM;
using cpDataServices.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;
namespace cpDataASP.Middleware
{
public class LocalizationAndCurrencyMiddleware
{
private readonly RequestDelegate _next;
public LocalizationAndCurrencyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IUserService _userService, ILoginContextAccessor loginContext, IAuthorizationPolicyProvider policyProvider, IPolicyEvaluator policyEvaluator)
{
var policy = await policyProvider.GetDefaultPolicyAsync();
await policyEvaluator.AuthenticateAsync(policy, context);
var localizationResources = await _userService.GetLocalizationResources();
loginContext.Timezone = localizationResources.Timezone;
CultureInfo.CurrentCulture = localizationResources.Culture;
await _next.Invoke(context);
}
}
}

Custom endpoint for authorized clients on Identity Server 4

I want my Identity Server 4 server to offer an additional service (e.g., "MyAdditionalService") for SOME of the registered clients. That service will be consumed by them through a custom endpoint to be defined on the server.
I am thinking of defining an API for my that service (e.g., named "myAdditionalService") so that the access to such service can be granted to clients according to their configuration. However I am not sure how to restrict the access to the Endpoint (MVC - Action method) allowing only the clients (potentially on behalf of a user) that are allowed to consume the API.
I found out that I can do:
services.AddAuthorization(options =>
{
options.AddPolicy("MyAdditionalServicePolicy",
policy => policy.RequireClaim("scope",
"myAdditionalService"));
});
and use the attribute [Authorize("MyAdditionalServicePolicy")] to decorate the action method that is used to access such service. However, I don't know can the server be the API at the same time or even if it is possible.
How can I implement this? It is confusing that the token service plays the role of the API as well, since it protects access to an action method or endpoint.
Thanks.
UPDATE:
My web app is an IdentityServerWithAspNetIdentity which already use the Authentication mechanism of Asp.net core Identity. For the sake of the example, the additional service my web app if offering to some registered clients is the list of Twitter friends of a user (Modeled on a controller called Twitter, action called ImportFriends) the api is consequently called "TwitterFriends"
As per suggestion in response below, I modified my Configure() method to have app.UseJwtBearerAuthentication(). I already had app.UseIdentity() and app.UseIdentityServer() as shown below:
app.UseIdentity();
app.UseIdentityServer();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AuthenticationScheme = "Bearer",
Authority = Configuration["BaseUrl"],
Audience = "TwitterFriends",
RequireHttpsMetadata = false //TODO: make true, it is false for development only
});
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
SignInScheme = "Identity.External", // this is the name of the cookie middleware registered by UseIdentity()
And on a dedicated controller:
[Authorize(ActiveAuthenticationSchemes = "Identity.Application,Bearer")]
//[Authorize(ActiveAuthenticationSchemes = "Identity.Application")]
//[Authorize(ActiveAuthenticationSchemes = "Bearer")]
[SecurityHeaders]
public class TwitterController : Controller
{...
but I am getting this in the log:
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware
[7]
Identity.Application was not authenticated. Failure message: Unprotect tic
ket failed
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed for user: (null).
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.A
uthorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes (Identity.Applicatio
n, Bearer).
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware
[12]
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[12]
AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action IdentityServerWithAspNetIdentity.Controllers.TwitterContro
ller.ImportFriends (IdentityServerWithAspNetIdentity) in 86.255ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 105.2844ms 401
I have tried different combinations of the attribute but it seems that Identity.Application and Bearer don't get along in this scenario: getting 401.
any help is appreciated.
Thanks..
See this example on how to host an API in the same web app as IdentityServer.
https://github.com/brockallen/IdentityServerAndApi
In essence you need to add the JWT token validation handler:
services.AddAuthentication()
.AddJwtBearer(jwt =>
{
jwt.Authority = "base_address_of_identityserver";
jwt.Audience = "name of api";
});
On the API itself you must select the JWT authentication scheme:
public class TestController : ControllerBase
{
[Route("test")]
[Authorize(AuthenticationSchemes = "Bearer")]
public IActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToArray();
return Ok(new { message = "Hello API", claims });
}
}
If you want to enforce an additional authorization policy, you can either pass that into the [Authorize] attribute or call it imperatively.
To achieve this, first you have to write some policy. Policy will define the boundry of accessibility of that specific api.
So you will assign the some scope to registered clients. let's say scope name is "ApiOnlyForRegisteredClients".
So we will create the policy as below:
services.AddAuthorization(options =>
{
options.SetRegisteredClientsPolicy();
}
and
private static void RequireScope(this AuthorizationPolicyBuilder authorizationPolicyBuilder, string[] values)
{
authorizationPolicyBuilder.RequireClaim("scope", values);
}
private static void SetRegisteredClientsPolicy(this AuthorizationOptions options)
{
options.AddPolicy(
OpenIdPolicies.Clients.RegisteredClients,
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireScope(new string[] { "ApiOnlyForRegisteredClients" });
});
}
Once it done, you are done with policy creation.
Make sure while creating the access token, you are put the same value "ApiOnlyForRegisteredClients" in scope claim.
Now we have to add one api and label it with [Authorize] attribute.
[Authorize(AuthenticationSchemes = "Bearer", Policy = OpenIdPolicies.Clients.RegisteredClients)]
public async Task<ActionResult<T>> Post(int userId, [FromBody] List<int> simRoleIds)
{
}
Now we have to add jwt authentication middleware.
.AddJwtBearer("Bearer", options =>
{
options.Authority = configuration["AuthorityAddresses"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["RequireHttpsMetadata"]);
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
TokenDecryptionKey = new X509SecurityKey()
ValidAudiences = apiResources.Select(x => x.ResourceName).ToList(),
ValidIssuers = new List<string> { authorityAddressWithHttps.Uri.OriginalString, authorityAddressWithBasePathHttps.Uri.OriginalString, configuration["AuthorityAddresses"] }
};
})

Resources