I am attempting to re-do a login system using Claims-Based auth.
Stepping through, it appears to correctly evaluate a username and password and correctly create a claims principal (including adding an authentication type in order to set IsAuthenticated to true, per this SO question.)
Somehow the identity doesn't seem to correctly get set on the wire. As a result, I'm redirected directly back to the login page.
I have the following in global.asax
:
private void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var currentPrincipal = ClaimsPrincipal.Current;
var transformer = new ClaimsTransformer(); //My own custom transformer; code below.
var newPrincipal = transformer.Authenticate(string.Empty, currentPrincipal); // does the transformation
// as I understand, it is proper & recommnded to set both of these
Thread.CurrentPrincipal = newPrincipal;
HttpContext.Current.User = newPrincipal;
}
In my Login Controller, I have a simple test against a membership DB. I verified while debugging that this has newCP
as a valid, authenticated identity that has the expected name.
[HttpPost]
[AllowAnonymous]
public ActionResult UserLogin(LoginViewModel viewModel)
{
var loginSuccess = Membership.ValidateUser(viewModel.UserName, viewModel.Password);
if (loginSuccess)
{
// CustomApplicationIdentity puts some identity-based logic into business domain terms and uses Claims underneath.
//Should have done it at the IPrincipal level, but instead I created the ToAuthenticatedStandardClaimsIdentity() which returns a new authenticated ClaimsIdentity.
var newIdentity = new CustomApplicationIdentity(viewModel.UserName);
var cp = new ClaimsPrincipal(newIdentity.ToAuthenticatedStandardClaimsIdentity());
var newCP = new ClaimsTransformer().Authenticate(string.Empty, cp);
System.Web.HttpContext.Current.User = newCP;
Thread.CurrentPrincipal = newCP;
if (!string.IsNullOrWhiteSpace(viewModel.ReturnUrl))
{
return Redirect(viewModel.ReturnUrl);
}
return RedirectToAction("Index", "Identity");
}
}
When it redirects to the Action, I see it hit the Application_PostAuthenticateRequest
again, which makes perfect sense.
However, despite previously setting the principal, this now appears to be an empty principal (no name, with IsAuthenticated set to false).
Some thoughts:
After a ton of research (and wading through the excellent Pluralsight Course by Dominick Baier), the solution was the following:
Step by step (again, much of this is credited to Dominick's video):
System.IdentityModel.Services
(full outline below, insert the two sections within that outline in your web.config):
<configuration>
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
</configuration>
(which depends on that config setting)
In the system.webServer
section of your web.config, add the following lines:
<remove name="RoleManager"/> <!--Not needed anymore in my case -->
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
(no longer needed because of SAM, which detects the cookie; why run it on every request if you don't have to, right?)
Add these lines in your ClaimsAuthenticationManager (mine was called ClaimsTransformer). I put this in a separate method called "EstablishSession", which took in my principal after it was already transformed:
private void EstablishSession(ClaimsPrincipal transformedPrincipal)
{
var sessionToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
}
So now the cookie is always set whenever you transform a claim, which makes sense because you're only transforming a claim if the user was successfully authenticated.
...wondering why SessionAuthenticationManager is always null.
Seriously, everything seems to work, and your config is correct, but darn it if it isn't null every. single. time.
Ahhhh, it appears that Cassini (the build in VS Debugger) doesn't work with SessionAuthenticationManager.
However, IIS Express does. Switch it to that in your project settings.
Now I have a page that works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With