Server side claims caching with Owin Authentication - asp.net

I have an application that used to use FormsAuthentication, and a while ago I switched it to use the IdentityModel from WindowsIdentityFramework so that I could benefit from claims based authentication, but it was rather ugly to use and implement. So now I'm looking at OwinAuthentication.
I'm looking at OwinAuthentication and the Asp.Net Identity framework. But the Asp.Net Identity framework's only implementation at the moment uses EntityModel and I'm using nHibernate. So for now I'm looking to try bypassing Asp.Net Identity and just use the Owin Authentication directly. I was finally able to get a working login using the tips from "How do I ignore the Identity Framework magic and just use the OWIN auth middleware to get the claims I seek?", but now my cookie holding the claims is rather large. When I used the IdentityModel I was able to use a server side caching mechanism that cached the claims on the server and the cookie just held a simple token for the cached information. Is there a similar feature in OwinAuthentication, or would I have to implement it myself?
I expect I'm going to be in one of these boats...
The cookie stays as 3KB, oh well it's a little large.
Enable a feature similar to IdentityModel's SessionCaching in Owin that I don't know about.
Write my own implementation to cache the information causing the cookie to bloat and see if I can hook it up when I configure Owin at application startup.
I'm doing this all wrong and there's an approach I've not thought of or I'm misusing something in Owin.
public class OwinConfiguration
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true,
CookieName = "Application",
ExpireTimeSpan = TimeSpan.FromMinutes(30),
LoginPath = "/Login",
LogoutPath = "/Logout",
ReturnUrlParameter="ReturnUrl",
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
//handle custom caching here??
}
}
//CookieName = CookieAuthenticationDefaults.CookiePrefix + ExternalAuthentication.ExternalCookieName,
//ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
}
}
UPDATE
I was able to get the desired effect using the information Hongye provided and I came up with the below logic...
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
var userId = context.Identity.GetUserId(); //Just a simple extension method to get the ID using identity.FindFirst(x => x.Type == ClaimTypes.NameIdentifier) and account for possible NULLs
if (userId == null) return;
var cacheKey = "MyApplication_Claim_Roles_" + userId.ToString();
var cachedClaims = System.Web.HttpContext.Current.Cache[cacheKey] as IEnumerable<Claim>;
if (cachedClaims == null)
{
var securityService = DependencyResolver.Current.GetService<ISecurityService>(); //My own service to get the user's roles from the database
cachedClaims = securityService.GetRoles(context.Identity.Name).Select(role => new Claim(ClaimTypes.Role, role.RoleName));
System.Web.HttpContext.Current.Cache[cacheKey] = cachedClaims;
}
context.Identity.AddClaims(cachedClaims);
}
}

OWIN cookie authentication middleware doesn't support session caching like feature yet. #2 is not an options.
#3 is the right way to go. As Prabu suggested, you should do following in your code:
OnResponseSignIn:
Save context.Identity in cache with a unique key(GUID)
Create a new ClaimsIdentity embedded with the unique key
Replace context.Identity with the new identity
OnValidateIdentity:
Get the unique key claim from context.Identity
Get the cached identity by the unique key
Call context.ReplaceIdentity with the cached identity
I was going to suggest you to gzip the cookie, but I found that OWIN already did that in its TicketSerializer. Not an option for you.

Provider = new CookieAuthenticationProvider()
{
OnResponseSignIn = async context =>
{
// This is the last chance before the ClaimsIdentity get serialized into a cookie.
// You can modify the ClaimsIdentity here and create the mapping here.
// This event is invoked one time on sign in.
},
OnValidateIdentity = async context =>
{
// This method gets invoked for every request after the cookie is converted
// into a ClaimsIdentity. Here you can look up your claims from the mapping table.
}
}

You can implement IAuthenticationSessionStore to store cookies into database.
Here's example for storing cookie in redis.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SessionStore = new RedisSessionStore(new TicketDataFormat(dataProtector)),
LoginPath = new PathString("/Auth/LogOn"),
LogoutPath = new PathString("/Auth/LogOut"),
});
Check out full example at here

Related

IIS / ASP.NET - How to Provide ClaimsPrincipal? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I want to have a (.NET Core) web api site. BEFORE a request comes into my site, it has to pass through a secondary site ("gateway"). The gateway will figure out the claims and create an IClaimsPrinciple with custom claims. As far as I am concerned, the gateway can set those claims by magic. My api will trust them 100%.
The gateway will then make its own request to my api, somehow attaching the claims info. When the request gets to my api site, the claims are already set.
How does the gateway site "attach" the claims to the http request?
An analogous (I think) use case is if IIS is set to windows auth. When I examine the static User object in my (unprotected) controller method, I can see it is a WindowsPrincipal, and its claims are things like AD user groups. My code (as far as I know) didn't do anything to the request to add those claims; it seems like IIS altered the request to attach those claims before it got to my site.
Is what I am asking possible? If so, how do you set the principle on a request? Or am I completely misunderstanding how principles are set? In the Windows Auth example, is it something in my .NET Project that's setting the WindowsPrinciple?
Are you asking, "how can I add custom claims to a user from inside an executable after the user has been authenticated but before the user tries to use a protected resource"?
By "protected resource" I mean anything protected with an [Authorize] attribute.
If that is what you are asking, I do not have a .Net Core example, but I do have a .Net Framework example, it is probably pretty easy to translate into .Net Core.
Yes, it is done in the middleware, in the Configuration method of your Startup class. In the example below I'm using Auth0 to do authentication. Below is the entire method, but scroll down to "SecurityTokenValidated" to see an example of adding a claim to an identity. In an actual application you would probably pull some unique key from the identity and then look up claims in a database.
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Configure Auth0 parameters
string auth0Domain = ConfigurationManager.AppSettings["auth0:Domain"];
string auth0ClientId = ConfigurationManager.AppSettings["auth0:ClientId"];
string auth0ClientSecret = ConfigurationManager.AppSettings["auth0:ClientSecret"];
string auth0RedirectUri = ConfigurationManager.AppSettings["auth0:RedirectUri"];
string auth0PostLogoutRedirectUri = ConfigurationManager.AppSettings["auth0:PostLogoutRedirectUri"];
// Enable Kentor Cookie Saver middleware
app.UseKentorOwinCookieSaver();
// Set Cookies as default authentication type
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
LoginPath = new PathString("/Account/Login")
});
// Configure Auth0 authentication
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "Auth0",
Authority = $"https://{auth0Domain}",
ClientId = auth0ClientId,
ClientSecret = auth0ClientSecret,
RedirectUri = auth0RedirectUri,
PostLogoutRedirectUri = auth0PostLogoutRedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid profile",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = notification =>
{
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var logoutUri = $"https://{auth0Domain}/v2/logout?client_id={auth0ClientId}";
var postLogoutUri = notification.ProtocolMessage.PostLogoutRedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
{
if (postLogoutUri.StartsWith("/"))
{
// transform to absolute
var request = notification.Request;
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
}
logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
}
notification.Response.Redirect(logoutUri);
notification.HandleResponse();
}
return Task.FromResult(0);
},
//this fires when a user is redirected to Auth0 for authentication.
SecurityTokenValidated = (context) =>
{
var identity = context.AuthenticationTicket.Identity;
var uniqueKey = identity.FindFirst("MyUniqueKey");
//lookup something in database using unique key
identity.AddClaim(new System.Security.Claims.Claim(ClaimTypes.Role, "SomeRole"));
return Task.FromResult(0);
}
}
});
}
}
}

Identity Server: Access tokens/items set in AuthorizationProeperties in ExternalLoginCallback on the client

Question
I have an identity server implementation that is being used by a number of applications in test and production. I am currently working on a new feature, where the client application using the identity server can perform Azure service management REST api calls. For this, it needs a token. I can generate this token, store it and even access it in the AccountController in the identity server.
My issue is figuring out how to send this to the client. I don't think this token belongs in the claims for the user. So I tried to add it as part of AuthenticationProperties as a token, but I cannot seem to access it in the client. Should I store it in a session like this SO user did link? There is one answer to this question, but that does not seem right (I even tried it out of desperation!)
Relevant sections of code
Generate the token
var resource = "https://management.azure.com/";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
// Acquire the token for the resource and save it
}
}
}
Restore it in AccountController
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
string resource = "https://management.azure.com/";
// snip
result = await authContext.AcquireTokenSilentAsync(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// snip
AuthenticationProperties props = null;
var tokens = new List<AuthenticationToken>();
var id_token = info.Properties.GetTokenValue("id_token");
if (id_token != null)
{
tokens.Add(new AuthenticationToken { Name = "id_token", Value = id_token });
}
if (result != null)
{
tokens.Add(new AuthenticationToken { Name = "management_token", Value = result.AccessToken });
}
if (tokens.Any())
{
props = new AuthenticationProperties();
props.StoreTokens(tokens);
}
// snip
// Can I access these "props" on the client? I even tried adding it to `Items`, no luck.
await HttpContext.Authentication.SignInAsync(user.UserId, user.DisplayName, provider, props, additionalClaims.ToArray());
}
So, my question, is this the right way go about it? If so, how do I access the authentication properties set? Or should I try saving this in the Session? If so, how do I store it in the client's session?
Any pointers would help. Thank you!
Just wanted to post an answer so that people wanting the same can benefit.
A token cache can be implemented to achieve this. This repository explains how.
Pay special attention to the AdalDistributedTokenCache linked here

How to Release a change that renames an User Role name

We're working on changes to an ASP.NET MVC app.
We're using Owin and OAuth2 to manage User permissions, but are managing the User DB object ourselves.
We have these on App Startup:
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(GetCookieAuthenticationOptions(AuthenticationType))
.UseOpenIdConnectAuthentication(GetOpenIdConnectOptions(AuthenticationType));
And we manually assign Claims to users when they log in Role is an enum:
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, user.Role.ToString()));
If more detail is needed, the auth code is included at the end.
All of this is has been working fine, but we need to rename a role.
The code rename is trivial, and it all works just fine when I log in after the role is renamed. But if I'm already logged in, when the code changes, then my old role Claim string is still in my Auth Cookie, and is no longer recognised by the Auth code.
Becuase I'm already logged in, it doesn't take me to the LogIn page - it just shows me the "Forbidden" error page (As though I'd entered a link to a page I shouldn't have visited)
And because our Auth works by checking whether you have "Role 'x' or any Role greater than 'x'", thus we get Forbidden on every page (because now the user doesn't have any Role and thus fails every Auth test, because their Role isn't recognised as passing any test.
As a result the user has no way to log out.
As a developer, I can wipe my browser cookies and log in from scratch (at which point it works just fine) but a normal user (probably?) won't be able to do that.
My first thought was do somehting like this: http://www.britishdeveloper.co.uk/2010/09/force-client-refresh-browser-cache.html, to all users to log out and get them to log in again, once after the release.
Unfortunately, since EVERY page will fail, I've got nowhere to put that code that will run for the relevant users :(
I could hack around with the Authentication Code so that it knows about the old Roles and grants that Claim permission, but that seem hideous.
Another option would be to modify the Authorisation code so that it logged users out if they don't have any recognised Roles, but that doesn't really feel right either, for some reason I can't put my finger on.
Any suggestions or opinions about the right way to release such a change?
=-=-=-=-=-=-=-=-=-=
Auth code:
private const string AuthenticationType = "FrontEnd" + CookieAuthenticationDefaults.AuthenticationType;
private const string IdTokenClaimName = "id_token";
public void Configuration(IAppBuilder app)
{
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(GetCookieAuthenticationOptions(AuthenticationType))
.UseOpenIdConnectAuthentication(GetOpenIdConnectOptions(AuthenticationType));
}
private static CookieAuthenticationOptions GetCookieAuthenticationOptions(string authenticationType)
{
return new CookieAuthenticationOptions
{
AuthenticationType = authenticationType,
};
}
private OpenIdConnectAuthenticationOptions GetOpenIdConnectOptions(string authenticationType)
{
return new OpenIdConnectAuthenticationOptions
{
Authority = AuthenticationConstants.AuthenticationAuthority,
ClientId = AuthenticationConstants.ClientId,
RedirectUri = AuthenticationConstants.ClientRedirectUrl,
ResponseType = "id_token",
Scope = "openid profile email",
SignInAsAuthenticationType = authenticationType,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n => Task.Run(() => AuthorizeIfUserExists(n)),
RedirectToIdentityProvider = n => Task.Run(() => SendIdTokenToLogout(n))
}
};
}
private static void SendIdTokenToLogout(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> n)
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst(IdTokenClaimName).Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
}
private void AuthorizeIfUserExists(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> authContext)
{
var identity = authContext.AuthenticationTicket.Identity;
var userIdClaim = GetUserIdClaim(identity);
var emailClaim = GetEmailClaim(identity);
var claimsIdentity = new ClaimsIdentity(
identity.AuthenticationType,
ClaimTypes.Name,
ClaimTypes.Role);
claimsIdentity.AddClaim(new Claim(IdTokenClaimName, authContext.ProtocolMessage.IdToken));
claimsIdentity.AddClaim(userIdClaim);
claimsIdentity.AddClaim(emailClaim);
using (var context = new DbDataContext())
{
var user = GetAndInitializeUserIfNecessary(context, userIdClaim.Value, emailClaim.Value);
// We add role and name claims to all successful logins that are also registered in our database.
if (user != null && !user.IsSoftDeleted)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, user.Role.ToString()));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, String.Format("{0} {1}", user.FirstName, user.Surname)));
}
}
authContext.AuthenticationTicket = new AuthenticationTicket(
claimsIdentity,
authContext.AuthenticationTicket.Properties);
}
I could hack around with the Authentication Code so that it knows about the old Roles and grants that Claim permission, but that seem hideous.
That seems best to me.
You have made a change which breaks backwards compatibility for users with active sessions. The usual approach for zero-downtime in that general case is to release code which supports both old and new clients, until you are sure that there are no old clients remaining, then delete the legacy code.

Make custom request when auth session is expired or user logged out

In one MVC project, I implemented asp.net identity based on cookies. Now I have a requirement to make a request to remote service when auth session is expired or when user logged off.
Is there any natural way to accomplish that? For now I managed to set two delegate properties from CookieAuthenticationProvider like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
CookieSecure = CookieSecureOption.SameAsRequest,
ExpireTimeSpan = TimeSpan.FromMinutes(expireInMinutes),
CookiePath = cookiePath,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = c =>
{
if (c.Properties.ExpiresUtc.HasValue && c.Properties.ExpiresUtc.Value < c.Options.SystemClock.UtcNow)
{
c.RejectIdentity();
c.Request.Context.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return Task.FromResult(0);
}
if (c.Options.SlidingExpiration)
{
// Reissue the auth cookie
}
return Task.FromResult(0);
},
OnResponseSignOut = c =>
{
// Make a custom request
}
}
});
At first glance it looks like it works but I don't like the idea of checking expiry date in here. Problem is that OnResponseSignOut is not called when auth cookie is simply expired but is called only when I explicitly call IAuthenticationManager.SignOut.
Is creating a custom CookieAuthenticationProvider the best option in here, or maybe there is another clean and natural solution for that case?

How should I be handling authentication with Identity 2.0 and WebAPI 2.1 and Owin 2?

I'm using
New browser only clients on the same domain
Identity 2.0
WebAPI 2.1
Owin 2.1
AngularJS front-end for registration, login and data display
In a WebAPI application with an AngularJS front-end.
I'm reading about token authentication but I am very confused now and I cannot find any good examples out there that use my combination. What I would like to know is should I be using cookies or tokens for the authentication. Should I be using a Userfactory or the CreatePerOwinContext?
Here's what I have in my Startup.Auth.cs
public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/"),
Provider = new CookieAuthenticationProvider {
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
}
Here's my WebAPI config:
public static class WebApiConfig
{
public static void CustomizeConfig(HttpConfiguration config)
{
config.Formatters.Remove(config.Formatters.XmlFormatter);
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
json.SerializerSettings.Converters.Add(new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-ddTHH:mmZ" });
}
I saw some examples using this code but I am not sure how I can call this:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
Could I just replace the cookie authentication with this?
Not an expert, but in my dabbling I've found that tokens work great for api and from javascript to api, and traditional cookies lean mostly for a ui. Either or both will work depending on what your trying to do.
You can follow something like this link that does cookie for the ui and token for the api http://blog.iteedee.com/2014/03/asp-net-identity-2-0-cookie-token-authentication/
app.CreatePerOwinContext(ApplicationSession.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Token Authentication
app.UseOAuthBearerAuthentication(new OAuthBearerOptions());
I think you can set the cookie authentication options authentication type to bearer if you want bearer for both, but you would have to play with it. The token would be in the owincontext under ".AspNet.ExternalBearer".
I also think if you register the Identity 2.0 middleware i think it also registers the oauth middleware stuff so you don't need to register the oauthserver middleware yourself. Thats the OAuthAuthorizationServerOptions code you posted. You dont need it.
if the ui and api are in separate then its a bit harder if you want to do some sort of single sign on from the ui pass to the api. I would recommend looking at opensource identity server or authorization server from thinktecture.
If your set on owin middleware and Identity 2.0 you would need to make sure the token can be read by both application and api and you probably would need to implement ISecureDataFormat. But remember, decryption doesn't mean you can 100% trust a token, it should be signed and verified. Depends on your needs.
Sorry, I guess thats a long ramble... Good luck.

Resources