Invalid Token when confirming email address - Asp.Net Core - 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");

Related

Distributed IdentityServer4 .net core (multi instance) issues HttpContext must not be null

So we have a setup of IdentityServer4 with .net core, on only one instance everything works as expected, however when we decided to spin more instances of Identity Server, we randomly got issues when logging in or out from the client.
I followed these docs: Distributed IdentityServer
This is how I am adding IDS4
_identityBuilder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiResources(Configuration.GetSection("idServer:apiResources"))
.AddInMemoryApiScopes(Configuration.GetSection("idServer:apiScopes"))
.AddInMemoryClients(Configuration.GetSection("idServer:clients"))
.AddAspNetIdentity<HeimdallUserEntity>()
;
Also because the server will be distributed I also added this code, note that certificate below is shared between the instances (so every instance uses the same certificate)
_identityBuilder.AddSigningCredential(certificate);
services.AddDataProtection()
.SetApplicationName(Assembly.GetExecutingAssembly().FullName)
.PersistKeysToDbContext<MainDbContext>()
.ProtectKeysWithCertificate(certificate);
However even with this setup I am having issues (randomly) while logging in and out from the Client which uses PKCE. The issue i am having is i am getting this exception randomly:
HttpContext must not be null.
Which is being thrown from: Microsoft.AspNetCore.Identity -> SignInManager -> SignOutAsync()
and from: Microsoft.AspNetCore.Identity -> SignInManager -> SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable additionalClaims)
This exception is handled and thrown in the SignInManager.cs class right here:
public HttpContext Context
{
get
{
var context = _context ?? _contextAccessor?.HttpContext;
if (context == null)
{
throw new InvalidOperationException("HttpContext must not be null.");
}
return context;
}
set
{
_context = value;
}
}
Also note that the client_credentials work normally, I request a token and everything works fine with multiple instances/replicas.
:: UPDATE ::
I have finally found the issue, which has nothing to do with Identity Server, in our system we use Microsoft Orleans, and we have a Grain which injects the UserService and, the UserService injects the SignInManager, turns out the SignInManager requires the HttpContext to be able to resolve services, but since orleans does not provide an IHttpContextAccessor, the HttpContext can never be resolved :/
For now we are calling the UserService directly. But it would be nice to be able to create/find a SignInManager which would not depend on HttpContext (especially since it only uses it to resolve other services)
Are you using the same token signing credentials across the different instances and adding them using the AddSigningCredential method?
Don't think this is the core issue here but you should be aware of that when you use the PersistKeysToDbContext and share it across services, then there might also be a race condition when multiple services tries to write to the same table in the database. Especially at startup (when the DB is empty) and when the keys are rotated every 90 days.

How to bypass .NET Core 2.2 and 3.1 error handling and display standard IIS status code pages?

In Asp.NET Core 2.2 and 3.1 there is a method called Configure() in the Startup class where you declare what exception handling method you want to use. For example, if you want to send a custom response when an exception occurs, you can do the following:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor accessor, IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider)
{
app.UseExceptionHandler(delegate (IApplicationBuilder errorApp)
{
errorApp.Run(async delegate (HttpContext Context)
{
Context.Response.StatusCode = 500;
Context.Response.ContentType = "text/plain";
await Context.Response.WriteAsync("An error occurred.");
});
});
//var x = ((string)null).Length; <--if this is uncommented, the custom handler won't catch it
//other configuration settings go here
}
Asp.Net Core will default to a standard developer exception page and show all the details of the exception if you don't define a custom handler.
My question is, how do I disable both the .Net Core developer exception page and the custom exception handler, and just have the errors bubble up to IIS so the old-fashioned error pages display?
The reason for wanting to do this is because custom handlers defined in the Configure() method only take effect after the Configure() method has completed. This means any exception that occurs in the Configure() method (see the commented-out line in the example) will send the user a full-blown developer error page, and there is (as far as I've researched) no way to disable this detailed developer page.
Obviously, I don't want these error details to appear on a production site. I figure disabling the Asp.NET Core error handling mechanism altogether will allow for 100% control of exceptions using the standard IIS error pages.
If you don't change any configuration and keep it as default, I assume you run .NET Core with IIS in in-process mode. You can try to disableStartUpErrorPage in this guideline.
Remember, In-Process means your .NET Core process is running on the same process with IIS so whenever you got startup exception, it will be fallback into Program.Main, not in your ExceptionHandler delegate. That is the reason why user can see full stack trace.

ReflectionTypeLoadException in ASP.NET Core MVC application

I'm running into a problem running an ASP.NET Core 1.0 application targeting .NET Framework 4.6. The problem didn't occur until we tried to run the application on a server running Windows Server 2016. The app is hosted in IIS and I have the .NET Core 1.0 Windows Hosting Bundle installed on the server.
Upon loading the site a 500 error is returned and this is written to the Logs:
An unhandled exception has occurred: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. (fc7986d0)
System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
Researching this it appears to relate to a missing dll or mismatched version, and that I should look at the LoaderExceptions property to get more info, but I'm not sure how to do that in this instance. The log entry is created just from setting up the loggerFactory in the Configure() method of Startup.cs.
I tried adding an IExceptionFilter ActionFilter implementation and reading the LoaderExceptions property if the exception is of type ReflectionTypeLoadException, but it doesn't get hit when ran on the server.
Is there a way to drill down into the Exception to read the LoaderExceptions property (in a production environment, there is no error when running in Visual Studio so debugging didn't help), or else another way to troubleshoot the original error to determine what is wrong with the server setup?
Instead of using IExceptionFilter, I wrote my own Middleware for catching this sort of exception and was able to log each exception from the LoaderExceptions property and determine what my problem is. Here is what I added to log the LoaderExceptions:
public class ExceptionCatchMiddleware
{
private readonly RequestDelegate _delegate;
private readonly ILogger _logger;
public ExceptionCatchMiddleware(RequestDelegate requestDelegate, ILogger<ExceptionCatchMiddleware> logger)
{
_delegate = requestDelegate;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _delegate(context);
}
catch (ReflectionTypeLoadException e)
{
foreach (Exception ex in e.LoaderExceptions)
{
_logger.LogCritical(ex.Message + Environment.NewLine + ex.StackTrace);
}
}
}
}
And then I just needed to add the Middleware to the Configure() method in Startup.cs:
app.UseMiddleware<ExceptionCatchMiddleware>();
In my case it was a missing dll that wasn't included in the project but since it was in my dev machine's GAC it ran there just fine.

Silverlight fault propagation and UserNamePasswordValidator

Scenario is a Silverlight client using Wcf service & custom authentication. To mitigate the 500/200 status code problem (avoid EndPointNotFound exception) I've applied the SilverLightFaultBehaviour. However, this does not work with UserNamePasswordValidator - When a FaultException is thrown from Validate(), it is not caught by the SilverLightFaultMessageInspector's implementation of BeforeSendReply.
So far, the only workaround I've found is using the alternative client stack instead ( WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);), but there are complications with using it which can no longer be ignored as a lot of our clients are on virtual machines, the silverlight client keeps crashing ( Silverlight 5 - Debugging npctrl.dll crash , http://communities.vmware.com/thread/394306?tstart=0 ).
My primary motivation is that I want to be able to distinguish a failed login from a connection error (the following code is from a client-side async callback method, and only works with the Client stack):
if (e.Error is MessageSecurityException)
{
this.HasLoginFailed.Value = Captions.Login_FailedLogin;
}
else
{
this.HasLoginFailed.Value = Captions.Login_FailedConnection;
}
Is there any other way of modifying the message sent when throwing a FaultException from UserNamePasswordValidator? Or any conceptually different way of doing custom authentication rather than what I am using which enables me to modify the message status or to keep it 200, or just to be able to distinguish a connection failure from bad credentials?
my server-side code for usernamepassword reg:
var serviceCredential = host.Description.Behaviors.Find<ServiceCredentials>();
serviceCredential.UserNameAuthentication.UserNamePasswordValidationMode =
UserNamePasswordValidationMode.Custom;
serviceCredential.UserNameAuthentication.CustomUserNamePasswordValidator =
new MyValidator();
When you throw a FaultException from MyValidator, it is wrapped as the InnerException of a MessageSecurityException, that's probably why you weren't able to catch it directly as a FaultException.
To add some information to the fault you are throwing, what you can do is adding a FaultCode:
throw new FaultException(
"Invalid user name or bad password.",
new FaultCode("BadUserNameOrPassword")
);
Then, catch the exception client-side and retrieve your FaultCode:
try { ... }
catch (MessageSecurityException e)
{
FaultException fault = (FaultException) e.InnerException;
String faultCode = fault.Code.Name;
// you can now display a meaningful error with the faultCode
}
I hope it will help!

Is there a way to get error feedback on asynchronous WCF calls?

I have a WCF service which works 100% in the synchronous (blocking) mode and I now need to rework a call so that it uses the async pattern.
The service uses authentication and performs a chunked file transfer from client to server so I have reworked it to use the 'Begin' async prefix to kick off the call.
Now I'm testing for errors by deliberately mangling the user credentials which causes the call to timeout on each part of the file chunk it tries to transfer, which takes ages. The problem is that I don't get any error feedback and can't see how to get any if the async call fails. This leads to some very large files failing to upload at all, but the client being unaware of it as no exceptions are thrown.
I have the Debug->Exceptions->All CLR exceptions ticked to see if there are any exceptions being swallowed but still nothing.
So in summary, how do you get error feedback from async calls in WCF?
Thanks in advance,
Ryan
The server caches the exception for you and if you call the end operation completion method for your async call it will throw any exceptions that occured.
private void go_Click(object sender, EventArgs e)
{
client.BeginDoMyStuff(myValue, new AsyncCallback(OnEndDoMyStuff), null);
}
public void OnEndDoMyStuff(IAsyncResult asyncResult)
{
this.Invoke(new MethodInvoker(delegate() {
// This will throw if we have had an error
client.EndDoMyStuff(asyncResult);
}));
}