ASP.NET Core - getting a message back from AuthenticationHandler - asp.net

I have implemented a subclass of AuthenticationHandler. It returns AuthenticationResult.Fail("This is why you can't log in");
I would have expected this message to end up in the body, or at least in the HTTP status text, but instead I get a blank 401 response.
Is there any way to provide additional information for failed authentication attempts in ASP.NET core?

For changing the body or Http status, you could try Context.Response.
Here is a demo code:
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace TestIdentity
{
public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
{
public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
, ILoggerFactory logger
, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
await Context.Response.WriteAsync("This is why you can't log in");
return AuthenticateResult.Fail("This is why you can't log in");
}
}
}

Related

Exclude Controller from Middleware

I have wrote a Middleware which checks if Authorization Token is included in the header and based on that request are executed or returns error if token is missing. Now it is working fine for other Controllers.
But What should I do for Login/Registration Controller which don't required Authorization headers. How can I configure my Middleware to ignore these.
Current Implementation of MiddleWare to Check Headers for Authorization Token.
public class AuthorizationHeaderValidator
{
private readonly RequestDelegate _next;
private readonly ILogger<AuthorizationHeaderValidator> _logger;
public AuthorizationHeaderValidator(RequestDelegate next, ILogger<AuthorizationHeaderValidator> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
StringValues authorizationHeader;
Console.WriteLine(context.Request.Path.Value);
if (context.Request.Headers.TryGetValue("Authorization", out authorizationHeader))
{
await _next(context);
}
else
{
_logger.LogError("Request Failed: Authorization Header missing!!!");
context.Response.StatusCode = 403;
var failureResponse = new FailureResponseModel()
{
Result = false,
ResultDetails = "Authorization header not present in request",
Uri = context.Request.Path.ToUriComponent().ToString(),
Timestamp = DateTime.Now.ToString("s", CultureInfo.InvariantCulture),
Error = new Error()
{
Code = 108,
Description = "Authorization header not present in request",
Resolve = "Send Request with authorization header to avoid this error."
}
};
string responseString = JsonConvert.SerializeObject(failureResponse);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(responseString);
return;
}
}
}
Not sure why you need middleware to validate if the Authorization header is present. It's difficult to exclude the controllers this way as all requests will go through this middleware before they hit the MVC pipeline.
[Authorize] attribute will do the job for you, given that you have some form of authentication integrated. If you need to exclude the controllers which don't require authorization, you can simply add [AllowAnonymous] at the controller level or at the action method level. Please see the code snippet below from the Microsoft Docs
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}
If you must use a middleware, you can consider using it as an MVC filter, which means that it will be scoped to the MVC pipeline. For more details, please see this link. However, that will still not solve the problem to exclude the controllers without adding some convoluted logic, which can be quite complicated.
This is not a complete answer but only directions. Please post your code once you finish this task for next generations.
It seems you need a Filter and not Middlware as Middleware don't have access to rout data. Create new authorization filter by inheriting from Attribute and implementing IAuthorizationFilter or IAsyncAuthorizationFilter. There is only one method to implement
public void OnAuthorization(AuthorizationFilterContext context)
{
}
or
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
}
Decorate controllers and/or actions that you want to exclude from this logic with AllowAnonymousAttribute. Inside your OnAuthorization method check if current action or controller has AllowAnonymousAttribute and if it is return without setting Result on AuthorizationFilterContext. Otherwise execute the logic from you original Middleware and set Result property. Setting Result will short-circuit the remainder of the filter pipeline.
Then register your filter globally:
services.AddMvc(options =>
{
options.Filters.Add(new CustomAuthorizeFilter());
});
I have solved my problem by Implementing PipeLine
public class AuthorizationMiddlewarePipeline
{
public void Configure(IApplicationBuilder applicationBuilder)
{
applicationBuilder.UseMiddleware<AuthorizationHeaderValidator>();
}
}
And than I am using it like this on either Controller Scope or Method scope
[MiddlewareFilter(typeof(AuthorizationMiddlewarePipeline))]

where can be stored user info in .net core API project with angular

I have an angular project and i am using .net core 2.o Web API. I stored my user info in Jwt and i want log every database operation. I can access user info by sending jwt and taking from request.header in server side. But the problem is, where can be stored? In my old mvc projects, i could stored in session. But this project is API . And we work with JWT not session. How can i achieve that store UserInfo during the request start to end. And i want to access UserInfo from everywhere. This is my actionFilter:
public class TestFilterAttribute : System.Web.Http.Filters.FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
var requestedUserInfo= context.HttpContext.Request.Headers["Authorization"];
??????????????? = requestedUserInfo;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
My architecture is like that:
Contoller => Service => Repository.
So, i have to send parameters as UserInfo in all methods. So, i dont want add all methods parameter as UserInfo. So, i want to learn get rid of this problem.
MyController.cs
[HttpGet("GetAllStudents")]
public async Task<ServiceResult>GetAllStudents()
{
var requestedUserId= context.HttpContext.Request.Headers["Authorization"];
return await (studentService.GetAllStudents(requestedUserId));
}
My service.cs
public async Task<ServiceResult> GetAllStudents(int requestedUserId)
{
return await unitOfWork.studentRepo.GetAllStudents(requestedUserId);
}
My repository.cs
public async Task<List<Student>> GetAllStudents(int requestedUserId)
{
LogOperation(requestedUserId);
return context.Students.ToList();
}
You can see that every method sending requestedUserId. How can i get rid of this?
you don't store it, Angular has to send the JWT in every request and Asp.Net Core webapi has to open it, validate it, and then read the user that made the request from it.
I found the solution. Our user info already stored in HttpContext. "HttpContextAccessor" is what i was looking for. You can inject by dependency injection and then you can use from everywhere (forexample dbcontext class or repo class)
public class StudentService : IStudentService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public StudentService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task<List<Student>> GetAllStudents()
{
var requestedUserId= _httpContextAccessor.HttpContext.Headers["Authorization"];
LogOperation(requestedUserId);
return context.Students.ToList();
}
}

Global Error Logging in ASP.Net MVC 6

I'm testing out an MVC 6 Web Api and wanted to implement logging into a global error handler. Just guaranteeing no errors get out of the system without being logged. I created an ExceptionFilterAttribute and added it globally in the startup:
public class AppExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
//Notice pulling from HttpContext Application Svcs -- don't like that
var loggerFactory = (ILoggerFactory)context.HttpContext.ApplicationServices.GetService(typeof (ILoggerFactory));
var logger = loggerFactory.Create("MyWeb.Web.Api");
logger.WriteError(2, "Error Occurred", context.Exception);
context.Result = new JsonResult(
new
{
context.Exception.Message,
context.Exception.StackTrace
});
}
}
Now in the startup, I'm adding this filter in:
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new AppExceptionFilterAttribute());
});
This all seems kind of brute force...is there a better way to get here using MVC 6?
Things I don't like or am unsure about with this approach:
Don't like pulling DI from http context
Don't have much context about the controller that originated the error (perhaps I can get it from the context in some way).
The other option I can think of is having a base controller that accepts an ILoggerFactory that all controllers inherit from.
Was wondering if there was some kind of diagnostics middleware that would allow logging to be inserted...
You question has 2 parts. 1) DI injectable filters 2) Global error handling.
Regarding #1: You can use ServiceFilterAttribute for this purpose.
Example:
//Modify your filter to be like this to get the logger factory DI injectable.
public class AppExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly ILogger _logger;
public AppExceptionFilterAttribute(ILoggerFactory loggerfactory)
{
_logger = loggerFactory.CreateLogger<AppExceptionFilterAttribute>();
}
public override void OnException(ExceptionContext context)
{
//...
}
}
//Register your filter as a service (Note this filter need not be an attribute as such)
services.AddTransient<AppExceptionFilterAttribute>();
//On the controller/action where you want to apply this filter,
//decorate them like
[ServiceFilter(typeof(AppExceptionFilterAttribute))]
public class HomeController : Controller
{
....
}
You should be able to get the details of the controller from the ExceptionContext that is passed.
Regarding #2: From your previous post looks like you were playing with ExceptionHandlerMiddleware(source & extension source)...how about using that?...some info regarding it:
This middleware is generic and is applicable to any middleware which
is registered after it and so any concepts like controller/action
info is specific to MVC which that middleware wouldn't be aware of.
This middleware does not handle formatter write exceptions. You could
write your own buffering middleware where you can modify the response
body to be a buffered stream(MemoryStream) and let the MVC layer
write the response to it. In the case of formatter write exceptions,
you can catch it and send a 500 error response with details.
An alternative way to perform global error handling is by using a ILoggerProvider.
The advantage to logging exceptions in this way is that it also captures errors which occur in places that an attribute would not catch. For example, exceptions that occur in Razor code could also be logged.
Here's a basic example with dependency injection:
Provider
public sealed class UnhandledExceptionLoggerProvider : ILoggerProvider
{
private readonly IMyErrorRepository errorRepo;
public UnhandledExceptionLoggerProvider(IMyErrorRepository errorRepo)
{
// inject whatever you need
this.errorRepo = errorRepo;
}
public ILogger CreateLogger(string categoryName) =>
new UnhandledExceptionLogger(errorRepo);
public void Dispose()
{
}
}
Logger
public class UnhandledExceptionLogger : ILogger
{
private readonly IMyErrorRepository errorRepo;
public UnhandledExceptionLogger(IMyErrorRepository errorRepo)
{
this.errorRepo = errorRepo;
}
public IDisposable BeginScope<TState>(TState state) =>
new NoOpDisposable();
public bool IsEnabled(LogLevel logLevel) =>
logLevel == LogLevel.Critical || logLevel == LogLevel.Error;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
errorRepo.LogError(exception);
}
}
private sealed class NoOpDisposable : IDisposable
{
public void Dispose()
{
}
}
}
Startup
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddTransient<IMyErrorRepository, MyErrorRepository>();
services.AddTransient<UnhandledExceptionLoggerProvider>();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
UnhandledExceptionLoggerProvider provider)
{
loggerFactory.AddProvider(provider);
// ... all the rest of your startup code
}
I'm using ASP.NET Core, but this solution should work.
I have a Middleware that I created to log all requests that go through the pipeline. In their, I just wrapped it in a try catch so if it throws an exception, it logs to my database.
public async Task Invoke(HttpContext context)
{
var sessionId = GetSessionId(context);
var path = context.Request.Path;
var startTime = DateTime.UtcNow;
var watch = Stopwatch.StartNew();
try
{
await _next.Invoke(context);
watch.Stop();
}
catch (Exception exception)
{
watch.Stop();
await _errorRepo.SaveException(exception, context.Connection.RemoteIpAddress.ToString(), sessionId);
}
finally
{
#pragma warning disable 4014
_requestLogRepo.LogRequest(
sessionId,
context.User.Identity.Name,
context.Connection.RemoteIpAddress.ToString(),
context.Request.Method,
path,
context.Request.ContentType,
context.Request.ContentLength,
startTime,
watch.ElapsedMilliseconds);
#pragma warning restore 4014
}
}

Capture ALL WebAPI requests

I would like to capture and save in a log file all the requests that my WebAPI should handle.
Just tried to save the Request.Content from the controller constructor but unfortunately,
the request object is null from the controller constructor scope.
Hope to learn an efficient way to do it.
I would just hook into web api tracing...
http://www.asp.net/web-api/overview/testing-and-debugging/tracing-in-aspnet-web-api
From the above article, you can implement ITraceWriter like so. This example uses System.Diagnostics.Trace.WriteLine, but you could plug in writing to a file here as well.
public class SimpleTracer : ITraceWriter
{
public void Trace(HttpRequestMessage request, string category, TraceLevel level,
Action<TraceRecord> traceAction)
{
TraceRecord rec = new TraceRecord(request, category, level);
traceAction(rec);
WriteTrace(rec);
}
protected void WriteTrace(TraceRecord rec)
{
var message = string.Format("{0};{1};{2}",
rec.Operator, rec.Operation, rec.Message);
System.Diagnostics.Trace.WriteLine(message, rec.Category);
}
}
As you can see from the Trace method, you get access to the HttpRequestMessage here.
I ended up implementing middleware to deal with it.
public class GlobalRequestLogger : OwinMiddleware
{
public override Task Invoke(IOwinContext context)
{
// Implement logging code here
}
}
Then in your Startup.cs:
app.Use<GlobalRequestLogger>();

Asp.Net OData V4: How can I get exceptions to bubble up to a global handler?

I am relatively new to Asp.Net OData. What I have realized is that the ODataMediaTypeFormatter logs exceptions like JSON deserialization issues and does not throw an exception. So I created a ModelValidationFilterAttribute and enabled it globally through HttpConfiguration.
public class ModelValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response =
actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest,
actionContext.ModelState);
}
}
}
The problem with this is that things like System.InsufficientExecutionStackException also get sent down to the client using this validation filter.
I want model validation state to be sent to the client when there is an error in the model format/data. For code exceptions, I would prefer that the exception just bubble up to a global handler where I can log, check the exception type, and respond to the client accordingly.
You may want to create an exception filter like this:
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
then you can control how to respond to the client. In the above example, it send 501 to the client.
References:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
http://www.asp.net/web-api/overview/web-api-routing-and-actions/web-api-global-error-handling

Resources