After spending days googling around reading various articles and looking through the Katana source code I'm struggling to understand what feel like fairly basic concepts of how OWIN/Katana/Identity work.
In particular, I've been looking at using Azure Active Directory for authentication of employees, local accounts for authentication of other users external to the company and controlling authorisation through roles in the app.
//I don't understand what this line of code is for
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
//this appears to be needed for SSO via AAD to work and by default has an authentication type of "Cookies" and cookie name of "ASP.Net.Cookies"
//it is required for SSO via AAD to work and the cookie created appears to be the one required to keep track that a user is logged in
app.UseCookieAuthentication(new CookieAuthenticationOptions());
//This bit sets up the AAD connection
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = aadInstance + tenantid,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect(postLogoutRedirectUri);
return Task.FromResult(0);
}
}
});
//now comes the second call to using cookies - this cookie as an authenticationtype of "ApplicationCookie" and cookie name of "ExternalUser"
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "ExternalUser",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(24),
Provider = new CookieAuthenticationProvider
{
// Revalidate user every 30 mins
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ExternalUserManager, ExternalUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Reading through the source code app.Use appears to simply add object types and options to a queue in OWIN to be called at some later point when invoked. I'm therefore struggling to understand the order of requests and where cookies are specifically created. I can see that the OwinContext.Authentication appears to control logins by creating an AuthenticationResponseGrant but I can't see where these are then used to then create cookies or where the cookies are read.
I have managed to get everything working using above and then adding claims for Roles manually using:
var identity = HttpContext.User.Identity as ClaimsIdentity;
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties() { IsPersistent = false },new ClaimsIdentity[] { identity });
However, I would like to understand how this all integrates together much better than I do. What role does the AuthenticationType play? What reads this and differentiates between ApplicationCookie, Cookies, ExternalCookie etc.? How does 1 cookie get read when Azure AAD is being used but another for the ExternalUsers?
As an aside previous to this we were using 2 user databases, 1 for employees and 1 for external users but we struggled to get it to work successfully. The problem being that the authenticationprovider validator seemed to get overridden by the second use of app.UseCookieAuthentication and log-out other users after 30 mins as it failed. We tried to create a Custom SecurityStampValidator to cope with this but couldn't get this to work. It feels like all this is tied together.
All thoughts and links on how this all fits together appreciated. I've seen lots of bits and pieces but nothing seems to go into the detail of what is happening when and where.
Thanks.
Edit
In response to Juan's comment the previous approach using 2 databases had 2 usermanagers to handle the different types of user. The OWIN set-up then had 2 calls to app.UseCookieAuthentication. The second call being something like:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "InternalUser",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(24),
Provider = new CookieAuthenticationProvider
{
// Revalidate user every 30 mins
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<InternalUserManager, InternalUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
i.e. identical bar the different user classes. The problem was that OnValidateIdentity always failed after 30 mins for 1 user type.
Edit2:
Here's the full code of the setup of the old problem (but note my main issue is understanding how these various cookie options all work and interact as I am no longer using the below as we've moved to using Azure AD for internal users):
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ExternalContext.Create);
app.CreatePerOwinContext(InternalContext.Create);
app.CreatePerOwinContext<ExternalUserManager>(ExternalUserManager.Create);
app.CreatePerOwinContext<InternalUserManager>(InternalUserManager.Create);
app.CreatePerOwinContext<InternalSignInManager>(InternalSignInManager.Create);
app.CreatePerOwinContext<ExternalSignInManager>(ExternalSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "InternalUser",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(24),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<InternalUserManager, InternalUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "ExternalUser",
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(24),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ExternalUserManager, ExternalUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
}
}
Any logins of InternalUser would be auto logged out after 30 mins.
Edit3:
After reading around some more it seemed that the AuthenticationType was the key parameter. I therefore took that old code above and change the AuthenticationType to "InternalUserCookie" and "ExternalUserCookie" - I also added to the constructor of the signin managers a line to set the AuthenticationType here to these as well thinking this would match up. Making this change though just means the login isn't retained at all and no cookies...