Reputation: 153
In my project setup, i don't have an Identity Provider, instead, I use one of the cookie value set by our internal system for authentication and transform that values as Claims Identity.
Once the Claims Identity created, I am planning to write the identity to Session Cookie of the application, using the SessionAuthenticationModule, and MachineKeySessionSecurityTokenHandler as below.
SessionSecurityToken token = new SessionSecurityToken(principal);
var handler = new MachineKeySessionSecurityTokenHandler(new TimeSpan(3, 30, 30));
var securityToken = handler.WriteToken(token);
SessionAuthenticationModule sam = new SessionAuthenticationModule();
sam.CookieHandler.RequireSsl = false; // This is required only for localhost to work
sam.CookieHandler.Write(securityToken, "Token", DateTime.Today.AddDays(1));
However, when reading back the cookie setup, I am unable to parse that back to SessionSecurityToken or Claims Identity. Any help to read this cookie and transform that to claims would be of great help.
I used the following code snippet to reading the cookie back, but I am getting error at handler.ReadToken method saying " ID4008: 'SecurityTokenHandler' does not provide an implementation for 'ReadToken'." error message.
//Check if the CGX session cookie is available
SessionAuthenticationModule sam = new SessionAuthenticationModule();
sam.CookieHandler.Name = "Token";
sam.CookieHandler.RequireSsl = false;
var securityToken = sam.CookieHandler.Read(filterContext?.HttpContext.ApplicationInstance.Context);
if (securityToken != null)
{
var handler = new MachineKeySessionSecurityTokenHandler(new TimeSpan(3, 30, 30));
var tokenString = Convert.ToBase64String(securityToken);
var token = handler.ReadToken(tokenString) as SessionSecurityToken;
if (token != null) sam.AuthenticateSessionSecurityToken(token, true);
}
What would be the right approach to read and validate the cookie value which was set from the application. As I stated the MVC Application is responsible for creating the cookie and validating it on subsequent requests.
Upvotes: 0
Views: 1180
Reputation: 153
Finally, I've found a solution to get the claims identity working for my scenario.
This is to configure WIF 4.5 for mix of your custom or forms authentication, without having to setup Identity Providers (STS), your ASP.NET MVC application RP will setup the authentication and utilize it on subsequent requests on WebFarm Scenario.
Would like to add the answer so anyone in similar need can utilize this. Before I go to the code part, I will add the configurations that are critical in achieving the results as expected.
Add the following configs in the web.config.
This is to setup the identity related configurations for use,
<configSections>
<!--WIF 4.5 sections -->
<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>
Below one's are for defining the Security Token Handler and Cookie Handler,
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration>
<cookieHandler name ="YourTokenName" mode="Default" requireSsl ="false">
<chunkedCookieHandler chunkSize="3000"/>
</cookieHandler>
</federationConfiguration>
</system.identityModel.services>
Add the following configs for setting up machine key as I am using the MachineKeySessionSecurityTokenHandler for WebFarm Scenario, and include the SessionAuthenticationModule in system.web section to make it work on localhost IIS Express VS2015 as of writing this answer.
<system.web>
<machineKey decryptionKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" validationKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<httpModules>
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</httpModules>
</system.web>
The following configs are for IIS 7.0 or above for Web Farm servers,
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</modules>
</system.webServer>
Once the configs are setup, now it's simple to access setup the Claims Identity, Claims Principal, Authentication, Security Token and Cookie. As follows,
If you use anti-forgery token for CORS in your web application you need to define the UniqueClaimTypeIdentifier set tell Anti-forgery token generator to use the unique identifier part of machine key encryption, otherwise anti-forgery wont work with Claims Identity.
In Global.asax
// Set Unique identifier for Antiforgery Token Generator
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
Place the below snippet in Login method or any code path where you kick start authentication,
// Step 1: Setup Identity
var appClaims = new List<Claim>{
new Claim(ClaimTypes.Name, "Your name claim"),
new Claim("UserId", "User ID claim"),
new Claim(ClaimTypes.NameIdentifier, "Name Identifier Claim Must"),
}
ClaimsIdentity identity = new ClaimsIdentity(appClaims,"Name Of Identity");
// Step 2: Setup Principal
ClaimsPrincipal principal = new ClaimsPrincipal(claimsIdentity);
//Step 3: The below code path sets the claims principal, using SessionAuthenticationModule, authenticates the principal and sets to httpcontext and current thread, sets the cookie in browser.
var authedCp = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate("Name", principal);
var token = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(authedCp, "Issuer Name", DateTime.UtcNow, DateTime.UtcNow.AddHours(1), false);
// This is only for debug mode in localhost, to make cookie written for local http or for https set it true
FederatedAuthentication.SessionAuthenticationModule.CookieHandler.RequireSsl = false;
FederatedAuthentication.SessionAuthenticationModule.AuthenticateSessionSecurityToken(token, true);
Place this snippet, where you need to validate if the cookie contains the Claims details and authenticate again on subsequent requests (action filter maybe),
//Check if the session cookie is available
SessionAuthenticationModule sam = FederatedAuthentication.SessionAuthenticationModule;
sam.CookieHandler.Name = "Token Name";
sam.CookieHandler.RequireSsl = false; //for local host, https make it true
var securityToken = sam.CookieHandler.Read(filterContext?.HttpContext.ApplicationInstance.Context);
if (securityToken != null)
{
SessionSecurityToken sessionToken = null;
var readStatus = sam.TryReadSessionTokenFromCookie(out sessionToken);
if (sessionToken != null)
{
sam.AuthenticateSessionSecurityToken(sessionToken, true);
}
}
Now you have the Claims Principal and Claims Identity setup on your HttpContext and Thread from the cookie stored on subsequent requests. You can access it anytime within the request pipeline, watch out for the cookie size when you add more claims into the Identity.
I would like to thank Sander for his blog post https://itq.nl/mixing-forms-authentication-with-claims-based-authorisation-in-asp-net/ that helped me setup later pieces of accessing the tokens..
Upvotes: 0