I am using JwtBearer authentication to secure my API. I am adding [Authorize] above each API and it worked.
I am using this code to add the authentication in the startup:
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:1234";
options.RequireHttpsMetadata = false;
options.Audience = "test";
});
I want a way to add the [Authorize] to a function in a service, or write a code in the function that works the same as [Authorize].
Using [Authorize] without passing any parameters boils down to a call that checks whether or not the user is authenticated. From inside a service, that would look something like this:
// If any of the properties being accessed are null, assume that the user
// is not authenticated.
var isAuthenticated = httpContext?.User?.Identity?.IsAuthenticated ?? false;
To access HttpContext inside of a service, you can use IHttpContextAccessor. Here's a complete example:
public class Service
{
private readonly IHttpContextAccessor httpContextAccessor;
public Service(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void ServiceFunction()
{
var httpContext = httpContextAccessor.HttpContext;
var isAuthenticated = httpContext?.User?.Identity?.IsAuthenticated ?? false;
if (isAuthenticated)
{
// The user is authenticated.
}
}
}
If you want to apply an authorisation policy, you can use IAuthorizationService. Here's a complete example of that:
public class Service
{
private readonly IHttpContextAccessor httpContextAccessor;
private readonly IAuthorizationService authzService;
public Service(IHttpContextAccessor httpContextAccessor,
IAuthorizationService authzService)
{
this.httpContextAccessor = httpContextAccessor;
this.authzService = authzService;
}
public async Task ServiceFunction()
{
var httpContext = httpContextAccessor.HttpContext;
var isAuthenticated = httpContext?.User?.Identity?.IsAuthenticated ?? false;
if (isAuthenticated)
{
// The user is authenticated.
var authzResult = await authzService.AuthorizeAsync(
httpContext.User,
"PolicyName");
if (authzResult.Succeeded)
{
// The user is authorised.
}
}
}
}
Note: To use IHttpContextAccessor, you might need to add services.AddHttpContextAccessor(); to your Startup.ConfigureServices method.
Related
I wish to check various session values from within my custom class. One thing I want to test is if the LoggedIn variable is set when a user hits a specific page. I want to do this in a class so I don't have to repeat code.
Here is how I've registered the service / IHttpContextAccessor:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<clsSessionHelper>();
services.AddRazorPages();
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/App/Mpl/MplHome/MplHome", "");
});
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(1200);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// End Session Support
services.AddMemoryCache();
}
Here is how I've build the custom class:
public class clsSessionHelper
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ISession _session;
public clsSessionHelper(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_session = _httpContextAccessor.HttpContext.Session;
}
public string getValue(string SessionKey)
{
string strResult = _session.GetString(SessionKey);
return strResult;
}
When I try to use the my helper class in a xx.cshtml.cs file like be below
clsSessionHelper objSessionHelper = new clsSessionHelper();
Test = objSessionHelper.getValue("LoggedIn");
I get an intellsense error "there is no argument given that corresponds to the required formal parameter 'httpContextAccessor' of 'clsSessionHelper.clsSessionHelper(HttpContextAccessor)'.
No doubt, I'm messing up the dependency injection. Any help would be greatly appreaciated.
The issue is that at the time the custom class is initialized and the IHttpContextAccessor injected, it is too early in the request process to have access to the HttpContext and by extension the ISession
Move any code that depend on the HttpContext out of the construct and into a member that is invoked during the context of a request.
public class clsSessionHelper : ISessionHelper {
private readonly IHttpContextAccessor httpContextAccessor;
public clsSessionHelper(IHttpContextAccessor httpContextAccessor) {
this.httpContextAccessor = httpContextAccessor;
}
public string getValue(string SessionKey) {
ISession session = httpContextAccessor.HttpContext.Session;
string strResult = session.GetString(SessionKey);
return strResult;
}
}
Also abstract your custom class
public interface ISessionHelper {
string getValue(string SessionKey);
}
so it too can be injected where needed.
//...
services.AddScoped<ISessionHelper, clsSessionHelper>();
//...
Avoid trying to initialize it manually.
private readonly ISessionHelper objSessionHelper;
public MyPageModel(ISessionHelper objSessionHelper) {
this.objSessionHelper = objSessionHelper;
}
public async Task<IActionResult> OnGet() {
var Test = objSessionHelper.getValue("LoggedIn");
//...
}
I want to authorize users to see only their own resources (e.g: Audits entity). So in the AuditController I have:
[MyAuthorize(Policy = nameof(ValidUserToSeeAuditAuthorizationHandler))]
[HttpGet]
public async Task<JsonResult<AuditView>> GetByIdAsync(Guid id)
{
// my business to fetch the audit info based by its id
// ...
return result;
}
Then I created my Requirement and AuthorizationHandler classes:
public class ValidUserToSeeAuditRequirment : IAuthorizationRequirement
{
public ValidUserToSeeAuditRequirment(Guid auditId)
{
auditId = auditId;
}
public Guid AuditId { get; }
}
public class ValidUserToSeeAuditAuthorizationHandler : AuthorizationHandler<ValidUserToSeeAuditRequirment>
{
private readonly AppUserManager _userManager;
private readonly IUnitOfWork _appDbContext;
public ValidUserToSeeAuditAuthorizationHandler(AppUserManager userManager, IUnitOfWork appDbContext)
{
_userManager = userManager;
_appDbContext = appDbContext;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserToSeeAuditRequirment requirement)
{
if (!context.User.IsAuthenticated())
{
context.Fail();
return Task.CompletedTask;
}
var theAudit = _appDbContext.Set<Audit>().SingleOrDefault(x => x.Id == requirement.AuditId);
var authenticatedUserId = Convert.ToInt32(context.User.GetSubjectId());
// If the authenticated user created the audit, then he/she is valid to see it
if (theAudit.SubjectauthenticatedUserId == authenticatedUserId)
{
// valid
context.Succeed(requirement);
return Task.CompletedTask;
}
// he/she is not authorized to see the resource (audit)
context.Fail();
return Task.CompletedTask;
}
}
But in the Startup class I want to configure authorization policies. How do I configure my Requirement class to get the user input parameters from the controller's action method?
services.AddAuthorization(options =>
{
// another policies
// ...
options.AddPolicy(name: nameof(ValidUserToSeeAuditAuthorizationHandler),
policy =>
{
policy.RequireAuthenticatedUser();
policy.AddRequirements(new ValidUserToSeeAuditRequirment( /****** HERE, how to pass the controller action method parameters ******/));
});
});
services.AddTransient<IAuthorizationHandler, ValidUserToSeeAuditAuthorizationHandler>();
You can customize a AuthorizationPolicy provider to get the parameter.
public class CustomAuthorizepolicyProvider: DefaultAuthorizationPolicyProvider
{
public CustomAuthorizepolicyProvider(IOptions<AuthorizationOptions> options):base(options)
{
}
public override Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (policyName=="[specified plicyname]")
{
var authorizePolicy = new AuthorizationPolicyBuilder();
authorizePolicy.AddRequirements(new ValidUserToSeeAuditRequirment(/* give the parameter*/)).Build();
return Task.FromResult(authorizePolicy);
}
return base.GetPolicyAsync(policyName);
}
}
Inject into startup. Note: it is a singleton.
services.AddSingleton<IAuthorizationPolicyProvider,CustomAuthorizepolicyProvider>();
I ended up with this solution:
/// <summary>
///
/// </summary>
public class ValidUserToSeeAuditRequirment : IAuthorizationRequirement
{
}
/// <summary>
/// Only an Admin and the authorized user can see the Audit
/// </summary>
public class ValidUserToSeeAuditAuthorizationHandler : AuthorizationHandler<ValidUserToSeeAuditRequirment>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ValidUserToSeeAuditAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserToSeeAuditRequirment requirement)
{
// If he has the Admin Role, then he can see the Audit
if (context.User.HasClaim(x => x.Type.ToUpperInvariant() == "ROLE" && x.Value.ToUpperInvariant() == "ADMIN"))
{
context.Succeed(requirement);
return;
}
// Get the audit id from the Routing
var auditIdFromRoute = _httpContextAccessor.HttpContext.GetRouteData()?.Values["id"].ToString();
if (auditIdFromRoute is null || !Guid.TryParse(auditIdFromRoute, out Guid requestingAuditId))
{
context.Fail();
return;
}
// get the authenticated user
var userId = Convert.ToInt32(context.User.GetSubjectId());
// check if the user has authorized to see the audit
if(isUserAllowToSeeAudit(userId, requestingAuditId))
{
context.Succeed(requirement);
return;
}
context.Fail();
}
private bool isUserAllowToSeeAudit(int userId, Guid auditId)
{
// ...
}
I would suggest you used the IAuthorizationRequirement and AuthorizationHandler approach. Instances of AuthorizationHandler (where T is the requirement) are registered as singletons in your startup. As such, you can inject an IHttpRequestAccessor into the handler, giving it the capability of accessing the request. This ends up being something like this
public class YourAuthorizationHandler : AuthorizationHandler<YourAuthorizationRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
YourAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, YourAuthorizationRequriement requirement)
{
var userIdInClaim = context.User.Claims.Where(claim => claimType == NameIdentifier).FirstOrDefault();
var request = _httpContextAccessor.HttpContext.Request;
request.EnableBuffering(); // allows the request to be read again
// read the request from request.Body assuming an HTTP POST. It will depend.
// do your logic checking the content here.
return context.Succeed(requirement); // Assuming things are what you want.
}
}
This is less complicated than it seems.
Read this Introduction to authorization in ASP.NET Core
I am currently trying to set up an Asp .net core 3 web project to connect to Google calendar and request user data after the user has logged in.
The user logs in and the application requests permission to access their data. The problems start after that.
start up
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
IConfigurationSection googleAuthSection = Configuration.GetSection("Authentication:Google");
options.ClientId = googleAuthSection["ClientId"];
options.ClientSecret = googleAuthSection["ClientSecret"];
options.Scope.Add(Google.Apis.Calendar.v3.CalendarService.Scope.Calendar);
});
}
controller
public class CalController : Controller
{
private readonly ILogger<CalController> _logger;
public CalController(ILogger<CalController> logger)
{
_logger = logger;
}
[Authorize]
public async Task<IActionResult> Index([FromServices] IGoogleAuthProvider auth)
{
var cred = await auth.GetCredentialAsync();
var service = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = cred
});
var calendar = await service.Calendars.Get("primary").ExecuteAsync();
return View();
}
}
Issue
Currently the system is looping on me. When i navigate to the calendar controller It comes with the following error.
So i created an account controller with the following action.
public IActionResult Logins([FromQuery] string returnURL)
{
return RedirectToAction("Index", "Cal");
}
Which now just causes the whole thing to loop. Why isn't the authorize attribute detecting that it is logged in and authenticated?
strange thing
If I remove the authorize attribute. Login the user and go directly to the cal controller i have access to their data and it all works.
But as soon as i add the authorize attributed it cant detect that it is authenticated.
Google .net client library.
I originally posted this over on the Google .net client libary 1584 unfortunately the team was not able to assist in getting this to work with asp .net core even though it is supposed to work.
I suspect there is something wrong with my setup but i am at a lost to figure out what the issue could be.
I finally got this working.
startup.cs
public class Client
{
public class Web
{
public string client_id { get; set; }
public string client_secret { get; set; }
}
public Web web { get; set; }
}
public class ClientInfo
{
public Client Client { get; set; }
private readonly IConfiguration _configuration;
public ClientInfo(IConfiguration configuration)
{
_configuration = configuration;
Client = Load();
}
private Client Load()
{
var filePath = _configuration["TEST_WEB_CLIENT_SECRET_FILENAME"];
if (string.IsNullOrEmpty(filePath))
{
throw new InvalidOperationException(
$"Please set the TEST_WEB_CLIENT_SECRET_FILENAME environment variable before running tests.");
}
if (!File.Exists(filePath))
{
throw new InvalidOperationException(
$"Please set the TEST_WEB_CLIENT_SECRET_FILENAME environment variable before running tests.");
}
var x = File.ReadAllText(filePath);
return JsonConvert.DeserializeObject<Client>(File.ReadAllText(filePath));
}
}
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.AddSingleton<ClientInfo>();
services.AddControllers();
services.AddAuthentication(o =>
{
// This is for challenges to go directly to the Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This is for forbids to go directly to the Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
var clientInfo = new ClientInfo(Configuration);
options.ClientId = clientInfo.Client.web.client_id;
options.ClientSecret = clientInfo.Client.web.client_secret;
});
services.AddMvc();
}
// 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(); });
}
}
Controller with Auth
[ApiController]
[Route("[controller]")]
public class GAAnalyticsController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public GAAnalyticsController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
// Test showing use of incremental auth.
// This attribute states that the listed scope(s) must be authorized in the handler.
[GoogleScopedAuthorize(AnalyticsReportingService.ScopeConstants.AnalyticsReadonly)]
public async Task<GetReportsResponse> Get([FromServices] IGoogleAuthProvider auth, [FromServices] ClientInfo clientInfo)
{
var GoogleAnalyticsViewId = "78110423";
var cred = await auth.GetCredentialAsync();
var service = new AnalyticsReportingService(new BaseClientService.Initializer
{
HttpClientInitializer = cred
});
var dateRange = new DateRange
{
StartDate = "2015-06-15",
EndDate = "2015-06-30"
};
// Create the Metrics object.
var sessions = new Metric
{
Expression = "ga:sessions",
Alias = "Sessions"
};
//Create the Dimensions object.
var browser = new Dimension
{
Name = "ga:browser"
};
// Create the ReportRequest object.
var reportRequest = new ReportRequest
{
ViewId = GoogleAnalyticsViewId,
DateRanges = new List<DateRange> {dateRange},
Dimensions = new List<Dimension> {browser},
Metrics = new List<Metric> {sessions}
};
var requests = new List<ReportRequest> {reportRequest};
// Create the GetReportsRequest object.
var getReport = new GetReportsRequest {ReportRequests = requests};
// Make the request.
var response = service.Reports.BatchGet(getReport).Execute();
return response;
}
}
I'm trying to add authentication to a ASP.NET Core 3.1 web service, that looks for a specific custom request header:
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
[HttpPut]
[Authorize(Policy = "MustSupplyAuthenticationToken")]
public async Task<ActionResult> putMyStuff()
{
// ...
I've configured "MustSupplyAuthenticationToken" to use my AuthenticationTokenRequirement class, and wired IAuthorizationHandler to use my AuthenticationTokenHandler (and wired up HttpContextFactory because I'm going to need it.):
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpContextAccessor();
services.AddAuthorization(options =>
{
options.AddPolicy("MustSupplyAuthenticationToken",
policy => policy.Requirements.Add(new AuthenticationTokenRequirement("MY_SECRET_TOKEN")));
});
services.AddTransient<IAuthorizationHandler, AuthenticationTokenHandler>();
}
My AuthenticationTokenRequirement is simple:
public class AuthenticationTokenRequirement : IAuthorizationRequirement
{
public readonly string authenticationToken;
public AuthenticationTokenRequirement(string authenticationToken)
{
this.authenticationToken = authenticationToken;
}
}
And my AuthenticationTokenHandler isn't much more complicated:
public class AuthenticationTokenHandler : AuthorizationHandler<AuthenticationTokenRequirement>
{
private readonly IHttpContextAccessor httpContextAccessor;
public AuthenticationTokenHandler(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
AuthenticationTokenRequirement requirement)
{
var httpContext = this.httpContextAccessor.HttpContext;
var request = httpContext.Request;
var authenticationToken = request.getRequestHeader("authenticationToken");
if (authenticationToken == null)
context.Fail();
else if (authenticationToken != requirement.authenticationToken)
context.Fail();
else
context.Succeed(requirement);
return Task.CompletedTask;
}
}
And the surprising thing is that it all works. HandleRequirementAsync() is called, and when I call context.Fail() access is denied and when I call context.Succeed() access is allowed.
The only problem is that when I call context.Fail() the response is coming back with HTTP 500 - and I need it to come back with HTTP 401. (And when I get this working, I'm going to need a different policy that returns a 403.)
Am I doing something wrong, and am getting a 500 because of some other error?
Or is a failed authentication policy is supposed return a 500?
What do I need to do to get it to return 401?
FWIW: I'm seeing this in my server logs:
2019-12-27T15:49:04.2221595-06:00 80000045-0001-f900-b63f-84710c7967bb [ERR] An unhandled exception has occurred while executing the request. (48a46595)
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatcher|8_0(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task`1 matcherTask)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
This could be why I'm seeing 500.
But only when I call context.Fail(). When I call context.Succeed() I don't.
So why am I getting "No authenticationScheme was specified" when I fail the requirement?
OK, for what I'm trying to do policies are not at all the correct approach.
What I need to do is to configure an authentication scheme.
After pulling out all of the authorization policy stuff above, I added this.
In the AuthorizeAttribute, I specify an authentication scheme:
[HttpPut]
[Authorize(AuthenticationSchemes = "AuthenticationTokenScheme")]
public async Task<ActionResult> putTicketDeliveryModel()
{
// ...
In ConfigureServices, I add the scheme:
services.AddAuthentication()
.AddScheme<AuthenticationTokenOptions, AuthenticationTokenHandler>("AuthenticationTokenScheme", _ => { });
And then I implement my AuthenticationTokenOptions and AuthenticationTokenHandler classes:
public class AuthenticationTokenOptions : AuthenticationSchemeOptions
{
}
public class AuthenticationTokenHandler : AuthenticationHandler<AuthenticationTokenOptions>
{
private readonly string expectedAuthenticationToken;
public AuthenticationTokenHandler(IOptionsMonitor<AuthenticationTokenOptions> optionsMonitor,
ILoggerFactory loggerFactory, UrlEncoder urlEncoder, ISystemClock systemClock,
IConfiguration config)
: base(optionsMonitor, loggerFactory, urlEncoder, systemClock)
{
this.expectedAuthenticationToken = config.GetSection("ExpectedAuthenticationToken").Get<string>();
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authenticationToken = this.Request.getRequestHeader("authenticationToken");
if (string.IsNullOrWhiteSpace(authenticationToken))
return Task.FromResult(AuthenticateResult.NoResult());
if (this.expectedAuthenticationToken != authenticationToken)
return Task.FromResult(AuthenticateResult.Fail("Unknown Client"));
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(Enumerable.Empty<Claim>(), Scheme.Name));
var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
}
This returns 401, if I pass the wrong token.
Still haven't figured out how to return 403.
In my asp.net core Web API i want to be sure that Users actually exist and are not deactivated when calling some Actions. We use JwtBearer authentication what works pretty fine. Our problem is that using only the Microsoft.AspNetCore.Authorization.AuthorizeAttribute it won't matter, if a user was deleted or deactivated after the token was issued (as long as the token it self is valid).
In my Startup.cs i configured a policy to solve this issue:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = AuthenticationHandler.TokenValidationParameters;
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
options.AddPolicy(nameof(ExistingUserRequirement),
policy => policy.Requirements.Add(new ExistingUserRequirement()));
});
services.AddTransient<IAuthorizationHandler, ExistingUserHandler>();
There i query the DataContext if the user is existing and active.
What works:
If I configure the Authorization attribute on an action everything works fine:
[HttpPost]
[Authorize(Policy = nameof(ExistingUserRequirement))]
public JsonResult Post([FromBody]CreateStoryViewModel viewModel) { ...}
I can access the requests user identity using HttpContextAccessor.HttpContext.User.Identity.Name.
What not works: If I configure the attribute on my BaseController the Identity-context seems the be missing when the ExistingUserHandler fires up:
[Authorize(Policy = nameof(ExistingUserRequirement))]
public abstract class BaseController : Controller { ... }
Now Identity.Name returns always NULL:
public class ExistingUserHandler : AuthorizationHandler<ExistingUserRequirement>
{
private IHttpContextAccessor HttpContextAccessor { get; }
private SaycleContext SaycleContext { get; }
public ExistingUserHandler(IHttpContextAccessor httpContextAccessor, SaycleContext saycleContext)
{
HttpContextAccessor = httpContextAccessor;
SaycleContext = saycleContext;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext authorizationContext, ExistingUserRequirement requirement)
{
var exists = false;
if (Guid.TryParse(HttpContextAccessor.HttpContext.User.Identity.Name, out var currentUserId))
{
exists = SaycleContext.Users.Any(u => Equals(u.Id, currentUserId));
}
if (!exists)
{
throw new NonExistingUserException();
}
authorizationContext.Succeed(requirement);
return Task.CompletedTask;
}
}
Am I missing anything? Why can the identity-context not be resolved using the Authorize attribute on my parent BaseController?