Oauth regenerateIdentityCallback not working - oauth

I'm using oauth and owin for my authentication in my app.
i want to regenerate identity with new claims in an interval but the method assigned to it's function handler is never been called
here is my code:
public static void ConfigureCookieAuthentication(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, PouyaKianParham.ContentProviderSystem.BusinessObjectLayer.Account.Models.ApplicationUser, int>(
validateInterval: TimeSpan.FromSeconds(15),
regenerateIdentityCallback: (a, b) => GenerateUserIdentityAsync(null,null),
getUserIdCallback: (id) => (id.GetUserId<int>()))
},
LoginPath = new PathString("/Account/Login")
});
}
And here is my callback method(which is also in OAuthconfig.cs):
public static async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager ApplicationUserManager, UserManager manager)
{
//regenrating identity with new claims
return identitywithClaim;
}
Thanks in advance

I've had the same problem, but the following worked for me.
I use a custom implementation of UserStore, UserManager and so on. It turned out, that the UserStore has to implement IUserSecurityStampStore<TUser, in TKey>.
After it was implemented, the regenerateIdentityCallback delegate is called when needed and everything works beautifully.

Related

Load Balancer breaks external login, because redirect uri is http (not https)

I am using ASP.NET MVC, and I am using MS built-in template to allow users to login with Facebook and Google, external ID. The external ID does not work on my Production environment (works on Test) and I believe this is because of the Load Balancer... (I am offloading SSL on load balancer and the backend servers use HTTP).
I have seen This Question and I think this is my problem: The login request contains a returnurl and because the backend server is using http, the return url is also http (not https). This is the method that I am talking about in AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
When user tries to login, I can see that redirect_uri is http (not https):
I have checked the question mentioned above and also this one. They both suggest that I should use relative path for redirect url in Startup.Auth, so this is what I have added to my code (ConfigureAuth method) :
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, long>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<long>()),
/* THIS IS THE CHANGE THAT I HAVE ADDED */
OnApplyRedirect = context =>
{
/* I have tried adding a breakpoint here, but it is never hit */
var redirectUri = new Uri(context.RedirectUri, UriKind.Absolute);
if (redirectUri.Scheme == "http" && redirectUri.Host == context.Request.Uri.Host)
{
context.RedirectUri = redirectUri.PathAndQuery;
}
context.Response.Redirect(context.RedirectUri);
}
}
});
But this change has no effect and the redirect_url remains http. If I put a break point on the change that I have added above, the breakpoint never hits... not sure where things are going wrong?
I found a solution using this question & this github issue:
Load Balancer would terminate the SSL and communicate with the backend server using http, but it would forward the original protocol (https, in this case) to backend server. So we could use x-forwarded-proto to detect the original protocol in the backend server:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
/* I HAVE ADDED THIS CODE */
app.Use((context, next) =>
{
if (context.Request.Headers["x-forwarded-proto"] == "https")
{
context.Request.Scheme = "https";
}
return next();
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, long>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<long>())
}
});
// more code...
}

Asp.net identity keep user login

I have to keep user login for 15 hours but its logout after about 20 minutes of inactivity.
My code in startup.auth.cs is
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromHours(15),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
}
}
Why user is logged out after about 20 minutes of inactivity?
As you say, you want to keep session active then put this following key in web.config.
maybe your application session get expired in 20 minutes(it is default value )
<sessionState timeout="900" cookieless="AutoDetect">

Owin, WebApi, and UseCookieAuthentication

I've configured OWIN in my ASP.NET MVC application using cookie authentication, but when I attempt to access an ApiController with an Authorize attribute on it, authorization fails and I can't figure out why. Stepping into the IsAuthorized method of the Authorize attribute, I can see that none of the identity properties that are present when accessing an MVC controller are present, so it certainly appears (at least to the authorize attribute) that the user is not authenticated.
The app is configured as follows:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(MyAuthContext.Create);
app.CreatePerOwinContext<MyUserManager>(MyUserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyUserManager, MyUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var httpConfig = new HttpConfiguration();
WebApiConfig.Register(httpConfig);
app.UseWebApi(httpConfig);
}
Do I absolutely have to use bearer tokens for WebAPI or is there just something I'm missing.

ExpireTimeSpan ignored after regenerateIdentity / validateInterval duration in MVC Identity (2.0.1)

Been scratching my head all day on this one. I'm trying to set up "very long" login sessions in MVC Identity 2.0.1. (30 days).
I use the following cookie startup:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
SlidingExpiration = true,
ExpireTimeSpan = System.TimeSpan.FromDays(30),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/My/Login"),
CookieName = "MyLoginCookie",
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Which on the whole, works fine. The cookie is set 30 days hence, all looks good.
If I close browser and come back after "validateInterval" duration has passed (30mins here) I'm still logged in, however the cookie is now re-issued as "session" only (correct cookie name still)! The 30 day expiration is gone.
If I now close browser/reopen again I'm no longer logged in.
I have tested removing the "Provider" and all works as expected then, I can come back several hours later and I'm still logged in fine.
I read that it is best practice to use the stamp revalidation though, so am unsure how to proceed.
When the SecurityStampValidator fires the regenerateIdentity callback, the currently authenticated user gets re-signed in with a non-persistent login. This is hard-coded, and I don't believe there is any way to directly control it. As such, the login session will continue only to the end of the browser session you are running at the point the identity is regenerated.
Here is an approach to make the login persistent, even across identity regeneration operations. This description is based on using Visual Studio MVC ASP.NET web project templates.
First we need to have a way to track the fact that a login session is persistent across separate HTTP requests. This can be done by adding an "IsPersistent" claim to the user's identity. The following extension methods show a way to do this.
public static class ClaimsIdentityExtensions
{
private const string PersistentLoginClaimType = "PersistentLogin";
public static bool GetIsPersistent(this System.Security.Claims.ClaimsIdentity identity)
{
return identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType) != null;
}
public static void SetIsPersistent(this System.Security.Claims.ClaimsIdentity identity, bool isPersistent)
{
var claim = identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType);
if (isPersistent)
{
if (claim == null)
{
identity.AddClaim(new System.Security.Claims.Claim(PersistentLoginClaimType, Boolean.TrueString));
}
}
else if (claim != null)
{
identity.RemoveClaim(claim);
}
}
}
Next we need to make the "IsPersistent" claim when the user signs in requesting a persistent session. For example, your ApplicationUser class may have a GenerateUserIdentityAsync method which can be updated to take an isPersistent flag parameter as follows to make such a claim when needed:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, bool isPersistent)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.SetIsPersistent(isPersistent);
return userIdentity;
}
Any callers of ApplicationUser.GenerateUserIdentityAsync will now need to pass in the isPersistent flag. For example, the call to GenerateUserIdentityAsync in AccountController.SignInAsync would change from
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent },
await user.GenerateUserIdentityAsync(UserManager));
to
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent },
await user.GenerateUserIdentityAsync(UserManager, isPersistent));
Lastly, the CookieAuthenticationProvider.OnValidateIdentity delegate used in the Startup.ConfigureAuth method needs some attention to preserve the persistence details across identity regeneration operations. The default delegate looks like:
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(20),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
This can be changed to:
OnValidateIdentity = async (context) =>
{
await SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(20),
// Note that if identity is regenerated in the same HTTP request as a logoff attempt,
// the logoff attempt will have no effect and the user will remain logged in.
// See https://aspnetidentity.codeplex.com/workitem/1962
regenerateIdentity: (manager, user) =>
user.GenerateUserIdentityAsync(manager, context.Identity.GetIsPersistent())
)(context);
// If identity was regenerated by the stamp validator,
// AuthenticationResponseGrant.Properties.IsPersistent will default to false, leading
// to a non-persistent login session. If the validated identity made a claim of being
// persistent, set the IsPersistent flag to true so the application cookie won't expire
// at the end of the browser session.
var newResponseGrant = context.OwinContext.Authentication.AuthenticationResponseGrant;
if (newResponseGrant != null)
{
newResponseGrant.Properties.IsPersistent = context.Identity.GetIsPersistent();
}
}
This bug is fixed in ASP.NET Identity 2.2. See https://aspnetidentity.codeplex.com/workitem/2319

Request.IsAuthenticated when using Identity with Owin for MVC and WebAPI

I have a ASP.Net WebAPI 2.1 that I have just transitioned across to using Identity 2.0 using bearer tokens. This works fine. Now I am attempting to pull in some MVC code to create a set of login and user management pages. My issue is that I can't seem to get Request.IsAuthenticated to work from my Razor views when I set the WebApi HttpConfigurationto SuppressDefaultHostAuthentication.
Below is my code, and I'm out of ideas as to how I can get this to work for both scenarios :(
Here's my Startup.cs which sets up the Identity OWIN module, and the WebAPI:
public class Startup
{
public void Configure(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
var httpConfiguration = new HttpConfiguration();
// Disable this line to allow Request.IsAuthenticated to work
// But by doing this, it allows the 'redirect' to kick in on unauthenticated API requests, which returns a HTML page for a webapi call, rather than the JSON 'unauthenticated' response
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
}
}
Here's my Global.asax.cs, which sets up the MVC side of things (AFAIK OWIN doesn't support any form of app.UseMvc()):
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
// pretty much the defaults here for everything, just renamed
AreaRegistration.RegisterAllAreas();
MvcConfig.ConfigureFilters(GlobalFilters.Filters);
MvcConfig.ConfigureRoutes(RouteTable.Routes);
MvcConfig.ConfigureBundles(BundleTable.Bundles);
}
}
Now in my Razor views, I would like to use Request.IsAuthenticated, as used in the Identity samples, but this fails when the httpConfiguration.SuppressDefaultHostAuthentication is enabled. I understand the goal of this extension is to remove the current identity after the Identity middleware has run -- so that the WebAPI authentication filter can do as it pleases. But I am hoping that on the MVC side of things, that the suppression wouldn't occur.
Example Razor view:
#if (Request.IsAuthenticated) // false when using httpConfiguration.SuppressDefaultHostAuthentication
{
<div>User.Identity.Email</div>
}
Can anyone help me? Is this even possible?
Thanks!
Looks like it's all about the ordering of the app builder. If I place the Identity bearer configuration before the WebAPI, then my WebAPI requests still utilize the Identity OWIN module. By placing the Cookie configuration after the WebAPI configuration, the Cookie Identity resolution occurs after the WebAPI identity removal, and before the MVC execution.
Unsure if this is the 'correct' way to go about doing it, but it seems to solve all of the test cases I have open.
public class Startup
{
public void Configure(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
var httpConfiguration = new HttpConfiguration();
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
}
}
EDIT
The above works, but it seems to be better to utilize the app.MapWhen() functionality to do this.
public class Startup
{
public void Configure(IAppBuilder app)
{
// setup auth for all requests
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
// setup webapi for only /api requests
app.MapWhen(
context => context.Request.Uri.PathAndQuery.StartsWith("/api"),
newApp => {
var httpConfiguration = new HttpConfiguration();
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
}
}
}

Resources