ASPNETCore API ExceptionFilterAttribute and IStringLocalizer - asp.net-core

I have a ExceptionFilterAttribute in my ASPNETCore API where I handle certain exception types.
For example any UnauthorizedAccessException I return HttpStatusCode.Unauthorized, any KeyNotFoundException I return HttpStatusCode.NotFound etc.
However, I'm also returning a basic message. For example, the string "Key Not Found" is returned with the HttpStatusCode.NotFound.
I would like the messages contained in the ExceptionFilterAttribute to be parsed using the IStringLocalizer. However, a ExceptionFilterAttribute doesn't seem to support dependency injection.
Has anyone encountered the same issue?
Is there an approach here which is considered best practice?

You can access the IServiceProvider from the ExceptionContext.
public override void OnException(ExceptionContext context)
{
var service = context.HttpContext.RequestServices.GetService<MyService>();
}

Related

.NET Core DI passing info of AddHttpContextAccessor to a service

In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens

Invalid Token when confirming email address - Asp.Net Core

I'm occasionally getting an "Invalid Token" error from my call to
userManager.ConfirmEmailAsync(user, token) . I've narrowed down the problem to the fact that my 2 web servers are sitting behind a load balancer, and the web server that generated the token isn't always the web server that is attempting to confirm the token. I had a similar problem with anti-forgery tokens in a different web site, which I fixed by persisting the data protection key to disk and sharing it between the web servers, so I tried a similar approach here.
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"c:\temp\API"));
I then copied the key to the same folder in my other web server but still wasn't successful. Debugging through the AspNetCore.Identity code I can see an exception thrown on the call to
var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token))
in the DataProtectorTokenProvider class. the catch block for the exception in Microsoft's code is simply
catch
{
// Do not leak exception
}
so I decided to inject an IDataProtector into my own Controller and try making that call myself.
public UserController(Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager, Microsoft.AspNetCore.Identity.RoleManager<IdentityRole> roleManager,
ILoggerFactory loggerFactory,
IDataProtectionProvider dataProtectionProvider)
{
Protector = dataProtectionProvider.CreateProtector("DataProtectorTokenProvider");
}
try
{
var unconverted = Convert.FromBase64String(request.EmailConfirmationToken);
var unprotectedData = Protector.Unprotect(unconverted);
}
catch (Exception e)
{
}
I can now catch the exception thrown on the Unprotect call and it's:
The payload was invalid
Microsoft.AspNetCore.DataProtection.Cng.CbcAuthenticatedEncryptor.DecryptImpl(Byte* pbCiphertext, UInt32 cbCiphertext, Byte* pbAdditionalAuthenticatedData, UInt32 cbAdditionalAuthenticatedData)\r\n at Microsoft.AspNetCore.DataProtection.Cng.Internal.CngAuthenticatedEncryptorBase.Decrypt(ArraySegment1 ciphertext, ArraySegment1 additionalAuthenticatedData)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(Byte[] protectedData, Boolean ignoreRevocationErrors, Boolean& requiresMigration, Boolean& wasRevoked)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)\r\n at VTR.API.Controllers.UserController.d__16.MoveNext() in C:\Projects\Brewster.Travel\src\cres\trunk\VTR.API\src\VTR.API\Controllers\UserController.cs:line 409
If I make that call with a token generated on the same server then it gets unprotected successfully. I obviously have some problem with how I'm attempting to share my data protection keys, if anyone could shed some light on my problem I would appreciate it.
I managed to get this working thanks to the documentation here: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview
I needed to add a call to SetApplicationName() in ConfigureServices:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"c:\someDirectory"))
.SetApplicationName("myApplicationName");

How to use exception mapper for exceptions thrown from interceptors in MSF4J

I added a authorisation module in interceptor, and tried to use exception mapper to throw 401 status code, but even if it is scanning my exception mapper, it is not using it in runtime.
#Component
public class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
public ExceptionMapper() {
System.out.println("true Manish= " + true);
}
#Override
public Response toResponse(Exception exception) {
System.out.println("product Manish Exception comning here");
return Response.status(Response.Status.EXPECTATION_FAILED).
entity(exception.getMessage()).
type("text/plain").
build();
}
}
So, it is printing true Manish=true, but when my interceptor throws an exception, it doesn't hit toResponse() method
At the moment MSF4J can't map the exception which are get thrown from interceptor level. It only can map the exceptions which are thrown inside the actual service. We would like to enhance that to having global exception mappers. https://github.com/wso2/msf4j/issues/327 been created for that.

Serialization error when specifying $select on ODataController endpoint

I've been developing a RESTful web service using Web API 2 and OWIN. Originally my controllers inherited from ApiController, and the GET actions supported OData filtering/queries i.e. marked with [EnableQuery].
We've now decided to look at whether it's feasible to expose a true OData service, and so make our controllers inherit from ODataController instead of ApiController. While this seems to be working well for the most part, $select no longer works.
public class BaseController : ODataController
{
... some properties here, not related to issue...
}
public class EmployeesController : BaseController
{
private readonly AppDbContext _context = new AppDbContext();
[EnableQuery]
public IQueryable<Employee> Get()
{
return _context.Employees;
}
...
}
The error I'm seeing is:
{
"error": {
"code": "",
"message": "An error has occurred.",
"innererror": {
"message": "'DbQuery`1' cannot be serialized using the ODataMediaTypeFormatter.",
"type": "System.Runtime.Serialization.SerializationException",
"stacktrace": " at System.Web.OData.Formatter.ODataMediaTypeFormatter.GetSerializer(Type type, Object value, ODataSerializerProvider serializerProvider)\r\n at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\r\n at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()"
}
}
}
I'm puzzled how this works with ApiController, but not with ODataController! Is there something I'm missing?
Cheers!
Ok, I've figured out what was happening:
I tried to update my OData packages to the latest version for OData v4 support. The Microsoft .Net OData library namespaces have changed between OData v3 (System.Web.Http.OData) and v4 (System.Web.OData). I had somehow managed to mix these libraries in such a way that I was referencing the EnableQuery attribute in the old OData library, which caused the serialization issue.
Not an obvious problem to track down - it's not obvious what's happening when the attributes have the same name but are in different namespaces and in fact belong to entirely different versions!

WCF UserName authentication and fault contracts

I have a WCF service configured to use custom UserName validation via the overriden Validate() method of the System.IdentityModel.Selectors.UserNamePasswordValidator class.
All methods of the contract have been decorated with the FaultContractAttribute to specify a custom SOAP fault as being returnable.
When throwing FaultException<T>, where T is the type specified in the FaultContractAttribute, everything behaves as expected and I get the custom fault in the response XML.
However, if I try and throw FaultException<T> in the overriden Validate() method of the username authentication class, I get a generic SOAP fault with the following reason:
"The creator of this fault did not specify a Reason."
However, if I change the code to throw the general SOAP fault as in:
throw new FaultException("Authentication failed.");
I will at least get "Authentication failed." in the reason element.
My questions are:
Why aren't the FaultException<T> exceptions treated the same if they're thrown in the Validate() as they are within the service implementation?
Is it possible to have exceptions thrown in the Validate() method conform to the FaultContractAttribute specified on the contract methods?
Any help greatly appreciated. My own guess is that the authentication comes before the message is associated with any method of the contract, and therefore, is not associated with the FaultContractAttribute, but any article confirming this and giving a workaround would be very useful.
Tali
It's a bit annoying but I got round it by doing this:
SecurityTokenValidationException stve
= new SecurityTokenValidationException("Invalid username or password");
throw new FaultException<SecurityTokenValidationException>(stve, stve.Message);
Including the message additionally means that you don't get the silly "did not specify a reason" message.
The problem is that the custom validation code is running outside of the context of any specific OperationContract, so there is no FaultContract is place for WCF to handle. So the short answer is no, you cannot get the exceptions thrown from your custom validator to honor the FaultContract.
You have a few options here. The one I prefer is to throw the non-generic FaultException and provide a pre-determined FaultCode; this way my catch blocks can differentiate contract faults from "plumbing" faults. Note that any exception you throw from a custom validator should come back as a MessageSecurityException, as shown below:
// Custom Validator:
public override void Validate(string userName, string password)
{
throw new FaultException(
"Invalid username or password.",
new FaultCode("AUTHENTICATION_FAILURE"));
}
// Client Code:
try
{
client.DoSomething();
}
catch ( MessageSecurityException ex )
{
var inner = ex.InnerException as FaultException;
if (inner != null && inner.Code.Name.Equals("AUTHENTICATION_FAILURE"))
{
// Security failure.
}
}
catch ( FaultException<SomethingFault> ex )
{
// Exception from the method itself.
}