In my current setup I am using a SPA (ReactJS) running on a different machine than my ASP.net Core Server. The whole client-server communication runs through SignalR except authentication. For security I want to use cookie-based authentication. As user-management I am using ASP.net.Core.Identity.
Setting up CORS works and now I'm implementing a login/logout mechanism based on simple HTTP-Request.
Here the workflow:
Send SignIn-Request to server via simple HTTP. (Works)
After successfully signin set cookie on client. (Works)
Client starts SignalR hub-connections (using the provided cookie). (Works)
Client sends data to server via HubConnetion. (Works)
Server sends data based on Claimspricipal. (Fails)
After the successfull authentication the client receives the cookie and uses it when negotiating with the signalR-hubs. When calling other controller-endpoints I have access to the ClaimsPrincipal which authenticated previously.
Now to my problem: when accessing the HubCallerContext the ClaimsPrincipal is not set (identity nor claims are set). Do I need to register the ClaimsPrincipal on this context as well or will it be handled by ASP.net? Other examples about authentication are assumuming that caller and hub are running on the same server. Did I missed or missunderstood something?
Here my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
services.AddScoped<IAuthorizationHandler, MyRequirementHandler>();
services.AddCors(
options => options.AddPolicy("CorsPolicy",
builder =>
{
builder
.SetIsOriginAllowed(origin =>
{
if (string.IsNullOrEmpty(origin)) return false;
if (origin.ToLower().StartsWith("http://localhost")) return true;
return false;
})
.AllowAnyHeader()
.AllowCredentials();
})
);
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
services.AddAuthorization(options =>
{
options.AddPolicy("PolicyName",
policy => { policy.Requirements.Add(new MyRequirement()); });
});
services.ConfigureApplicationCookie(config =>
{
config.Cookie.HttpOnly = false;
config.Cookie.Name = "my-fav-cookie";
config.ExpireTimeSpan = TimeSpan.FromDays(14);
config.Cookie.SameSite = SameSiteMode.None;
config.Cookie.SecurePolicy = CookieSecurePolicy.Always;
config.SlidingExpiration = false;
});
services.AddSignalR(opt => { opt.EnableDetailedErrors = true; })
.AddMessagePackProtocol(option => { option.SerializerOptions.WithResolver(StandardResolver.Instance); })
.AddNewtonsoftJsonProtocol(option =>
{
option.PayloadSerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
option.PayloadSerializerSettings.DateParseHandling = DateParseHandling.DateTime;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerManager logger,
ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
...some Hubendpoints...
});
}
Here the MyRequirement.cs:
public class MyRequirement : IAuthorizationRequirement
{
}
And here the MyRequirementHandler.cs:
public class
MyRequirementHandler: AuthorizationHandler<MyRequirement,
HubInvocationContext>
{
private readonly IAuthorizationHelper _authorizationHelper;
private readonly UserManager<ApplicationUser> _userManager;
public MyRequirementHandler(
IAuthorizationHelper authorizationHelper,
UserManager<ApplicationUser> userManager)
{
_authorizationHelper = authorizationHelper;
_userManager = userManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
MyRequirement requirement,
HubInvocationContext resource)
{
var user = context.User;
...
}
}
And the Hub.cs:
public class Hub: Hub<ISystemClient>
{
private readonly ISystemService _systemService;
public Hub(ISystemService systemService)
{
_systemService = systemService;
}
[Authorize("PolicyName")]
public async Task DoSthFancy()
{
var user = Context.User;
var fancyStruff = await _systemService.GetFancyStuff(user);
await Clients.Caller.SendFancyStuff(fancyStuff);
}
}
Thanks for your help!
EDIT (04-11-2022)
I edited/fixed the code-snippet of the Startup.cs due to bad copy-paste. Thanks for the hint.
After implementing an example step by step I figured out that ASP sets the ClaimsPrincipal inside the HubContext only when you are challeging the hub-endpoint or the hub itself by adding a [Authorization] attribute. So what was missing was a implementation of an AuthenticationHandler, a registration in the configuration and setting the authorization-attribute.
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public CustomAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager) : base(options, logger, encoder, clock)
{
_userManager = userManager;
_signInManager = signInManager;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (Context.Request.Cookies.TryGetValue("my-fav-cookie", out var cookie))
{
var user = Context.Request.HttpContext.User;
var ticket = new AuthenticationTicket(user, new(), CustomCookieScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
return Task.FromResult(AuthenticateResult.Fail("my-fav-cookie"));
}
}
In the Startup.cs you need to register the Authentication:
services.AddAuthentication().AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>(
"MyCustomScheme", _ => { });
and in the Hub:
namespace My.HubProject {
[Authorize(AuthenticationScheme="MyCustomScheme")]
public class MyHub : Hub {
...
}
}
Hope it will help others!
Related
For a school project I am remaking the Top2000 website (dutch website with the top 2000 most liked songs per year). Now I have a problem with the roles and the authorization.
I want to add an admin role and give only user with that role access to the privacy page.
This is what I got so far:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<db_a74225_top2000Context>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/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.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
HomeController.cs
namespace Top2000.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
[Authorize(Roles = "Admin")]
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Screenshot of dbo.AspNetUserRoles
Screenshot of dbo.AspNetUsers
Screenshot of dbo.AspNetRoles
I expect to get to the privacy page but when I login I still get Access Denied.
Screenshot of Access denied page
Judging from the screenshot of your database, you may not have successfully created a role, you can create a user with a role like the following method CreateRolesandUsers.
public class HomeController : Controller
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<IdentityUser> _userManager;
public HomeController(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
public async Task CreateRolesandUsers()
{
bool x = await _roleManager.RoleExistsAsync("Admin");
if (!x)
{
var role = new IdentityRole();
role.Name = "Admin";
await _roleManager.CreateAsync(role);
}
var user = new IdentityUser();
user.UserName = "123#123.com";
user.Email = "123#123.com";
string password = "Defaultpassword01!";
IdentityResult chkUser = await _userManager.CreateAsync(user, password);
if (chkUser.Succeeded)
{
var result = await _userManager.AddToRoleAsync(user, "Admin");
}
}
public IActionResult Index()
{
return View();
}
[Authorize(Roles = "Admin")]
public IActionResult Privacy()
{
return View();
}
}
Your DbContext:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Then in your startup change your code
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
to
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
When you access this method to successfully create a role, you can log in to the user and then access Privacy.
I am having trouble wiring up identity into Blazor server with ASP.NET Core identity. Specifically getting the correct logged in state in Blazor pages (while I am getting them from the Blazor pages).
I think it's related to some of the startup being initialized in another project - but not sure how to debug it or what the solution is to be able to get the logged in state correctly.
Reproduction steps and link to GH repo below as a POC.
Background
I'm porting over the clean-code project by JasonTaylor from Angular / ASP.NET Core to a Blazor server project with ASP.NET Core Identity.
Issue
The application runs up and I can browse the pages when I register I can see logged-in state in the identity-based default pages but in the Blazor pages that use the AuthorizeView (e.g. LoginDisplay.razor) it's not aware of being authorized.
Startup in the Blazor project:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication();
services.AddInfrastructure(Configuration);
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<ICurrentUserService, CurrentUserService>();
services.AddHttpContextAccessor();
services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
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.UseHealthChecks("/health");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
AddInfrastructure in another project references in startup:
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetValue<bool>("UseInMemoryDatabase"))
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("CleanArchitectureDb"));
}
else
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
}
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IDomainEventService, DomainEventService>();
services
.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<ApplicationUser>>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddTransient<IDateTime, DateTimeService>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<ICsvFileBuilder, CsvFileBuilder>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddAuthorization(options =>
{
options.AddPolicy("CanPurge", policy => policy.RequireRole("Administrator"));
});
return services;
}
}
public class RevalidatingIdentityAuthenticationStateProvider<TUser>
: RevalidatingServerAuthenticationStateProvider where TUser : class
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly IdentityOptions _options;
public RevalidatingIdentityAuthenticationStateProvider(
ILoggerFactory loggerFactory,
IServiceScopeFactory scopeFactory,
IOptions<IdentityOptions> optionsAccessor)
: base(loggerFactory)
{
_scopeFactory = scopeFactory;
_options = optionsAccessor.Value;
}
protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
// Get the user manager from a new scope to ensure it fetches fresh data
var scope = _scopeFactory.CreateScope();
try
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<TUser>>();
return await ValidateSecurityStampAsync(userManager, authenticationState.User);
}
finally
{
if (scope is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
scope.Dispose();
}
}
}
private async Task<bool> ValidateSecurityStampAsync(UserManager<TUser> userManager, ClaimsPrincipal principal)
{
var user = await userManager.GetUserAsync(principal);
if (user == null)
{
return false;
}
else if (!userManager.SupportsUserSecurityStamp)
{
return true;
}
else
{
var principalStamp = principal.FindFirstValue(_options.ClaimsIdentity.SecurityStampClaimType);
var userStamp = await userManager.GetSecurityStampAsync(user);
return principalStamp == userStamp;
}
}
}
Steps to reproduce
Register : https://localhost:44399/Identity/Account/Register
Browse to : https://localhost:44399/Identity/Account/Login - Notice username in header is populated from the ASP.Net Identity pages
Browse to : https://localhost:44399/ - Notice the Header is Register, Login, About (Based on https://github.com/davidshorter/CleanCodeBlazor/blob/Rework/src/Web/Shared/LoginDisplay.razor)
Pushed up my changes to GH if anyone fancies a
look : https://github.com/davidshorter/CleanCodeBlazor/tree/Rework
This was an issue with mixing IdentityServer and ASP.net identity.
By removing Microsoft.AspNetCore.ApiAuthorization.IdentityServer and the use of base class from ApiAuthorizationDbContext<ApplicationUser> back to IdentityDbContext<ApplicationUser> resolved this.
I have an Blazor webassembly application which uses IdentityServerJwt AddAuthentication and is working with Hangfire. I am trying to configure Hangfire to allow only users who are admins authorization based on the article here but I am getting an No authentication handler is registered for the scheme 'Bearer' error. What should I add as an AuthenticationSchemes. JwtBearerDefaults.AuthenticationScheme` does not work.
What am I missing?
public partial class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
string handfirepolicyname="HangfirePolicyName";
public void ConfigureServices(IServiceCollection services)
{
...Code removed for brevity
services.AddAuthentication().AddIdentityServerJwt();
services.AddAuthorization(options =>
{
options.AddPolicy(handfirepolicyname, builder =>
{ builder.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser();
builder.RequireRole("admin");
});
});
var hangfireConnectionstring = "SomeHangfireDatabaseConnectionString";
var mySqlStorageOptions = new MySqlStorageOptions();
var mySqlStorage = new MySqlStorage(hangfireConnectionstring, mySqlStorageOptions);
services.AddHangfire(config => config.UseStorage(mySqlStorage));
services.AddHangfireServer();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationIdentityDbContext identityDbContext)
{
...Code removed for brevity
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
//UseAuthentication, UseAuthorization should be before UseHangfireDashboard
app.UseHangfireDashboard();
app.UseEndpoints(endpoints =>
{
endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions()
{
Authorization = new List<IDashboardAuthorizationFilter> { }
}).RequireAuthorization(handfirepolicyname);
});
}
Error:
No authentication handler is registered for the scheme 'Bearer'. The registered schemes are: Identity.Application, Identity.External, Identity.TwoFactorRememberMe, Identity.TwoFactorUserId, idsrv, idsrv.external, IdentityServerJwt, IdentityServerJwtBearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("Bearer",...)?
by adding
[Authorize(AuthenticationSchemes = "Bearer")]
public class TestController : ControllerBase{}
on top of the controller
I am building authentication on my asp.net core application using the identity. I followed 2 different tutorials but in both cases, my app doesn't send cookies to the browser. I reckon I am missing something important but I don't know what. Could you point me in the right direction? Here is the relevant code:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<IdentityDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>();
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
public class HomeController : Controller
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
public HomeController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<IActionResult> SignUp()
{
await _userManager.CreateAsync(new IdentityUser("my_name"), "Secret123:");
return Ok("Signed up");
}
public async Task<IActionResult> SignIn()
{
var result = await _signInManager.PasswordSignInAsync("my_name", "Secret123:", true, false);
if(!result.Succeeded)
return Ok("Sign in failed");
return Ok("Signed in");
}
}
Edit: I created a fresh project and pasted these 2 classes, installed the same NuGet packages there, and the cookies worked. I have a problem somewhere else in the old project. I still have no clue where. I would appreciate if you comment where you think it might be.
I'm trying to access HttpContext to get RemoteIpAddress and User-Agent, but within Startup.cs.
public class Startup
{
public Startup(IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
Configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
public IConfiguration Configuration { get; }
public IHttpContextAccessor _httpContextAccessor { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
var key = Encoding.ASCII.GetBytes(Configuration.GetValue<string>("claveEncriptacion"));
var ip = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
//x.Audience = ip + "-" + userAgent;
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = true
};
});
With the previous code I have an error executing the project.
Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor' while attempting to activate 'JobSiteMentorCore.Startup'.'
According to the ASP.NET Core documentation , only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):
IWebHostEnvironment
IHostEnvironment
IConfiguration
So you cannot inject IHttpContextAccessor to Startup constructor.
However you can get DI resolved service in ConfigureServices method of the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IYourService, YourService>();
// Build an intermediate service provider
var serviceProvider = services.BuildServiceProvider();
// Resolve the services from the service provider
var yourService = serviceProvider.GetService<IYourService>();
}
But you can not get the HttpContext using IHttpContextAccessor similarly because HttpContext is null unless the code executed during any HttpRequest. So you have to do your desired operation from any custom middleware in Configure method of the Startup class as follows:
public class YourCustomMiddleMiddleware
{
private readonly RequestDelegate _requestDelegate;
public YourCustomMiddleMiddleware(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
}
public async Task Invoke(HttpContext context)
{
// Your HttpContext related task is in here.
await _requestDelegate(context);
}
}
Then in the Configure method of the Startup class as follows:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware(typeof(YourCustomMiddleMiddleware));
}
I've finally found a possible solution, using middleware to validate the token.
I created a class ValidationHandler, and this class can use HttpContext.
public class ValidationRequirement : IAuthorizationRequirement
{
public string Issuer { get; }
public string Scope { get; }
public HasScopeRequirement(string scope, string issuer)
{
Scope = scope ?? throw new ArgumentNullException(nameof(scope));
Issuer = issuer ?? throw new ArgumentNullException(nameof(issuer));
}
}
public class ValidationHandler : AuthorizationHandler<ValidationRequirement>
{
IHttpContextAccessor _httpContextAccessor;
public HasScopeHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
{
var ip = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
//context.Succeed(requirement);
return Task.CompletedTask;
}
}
Finally in the Startup.cs class it is necessary to add the following.
services.AddAuthorization(options =>
{
options.AddPolicy("read:messages", policy => policy.Requirements.Add(new HasScopeRequirement("read:messages", "david")));
});
Thank you vidgarga. I would upvote but I have a new account. Your answer helped me on my project. I'm coming from f# land so I am including my implementation for any fsharpers that need this solution.
type HasScopeHandler() =
inherit AuthorizationHandler<HasScopeRequirement>()
override __.HandleRequirementAsync(context, requirement) =
let scopeClaimFromIssuer = Predicate<Claim>(fun (c: Claim) -> c.Type = "scope" && c.Issuer = requirement.Issuer)
let userDoesNotHaveScopeClaim = not (context.User.HasClaim(scopeClaimFromIssuer))
let isRequiredScope s = (s = requirement.Scope)
let claimOrNull = context.User.FindFirst(scopeClaimFromIssuer)
if (userDoesNotHaveScopeClaim) then
Task.CompletedTask
else
match claimOrNull with
| null -> Task.CompletedTask
| claim ->
let scopes = claim.Value.Split(' ')
let hasRequiredScope = scopes.Any(fun s -> isRequiredScope s)
if (hasRequiredScope) then
context.Succeed(requirement)
Task.CompletedTask
else
Task.CompletedTask