ASP.NET MVC 5 Owin Identity got lost before allowed ExpiresUtc - asp.net-mvc

public void ConfigureAuth(IAppBuilder app)
{
app.UseKentorOwinCookieSaver(PipelineStage.Authenticate);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
LogoutPath = new PathString("/Logout"),
CookieSecure = CookieSecureOption.SameAsRequest ,
SlidingExpiration = true,
CookieName = ".app",
CookieHttpOnly = true,
CookiePath = "/",
CookieDomain = Domain
});
My sign in method:
private void IdentitySignin(AppUserState appUserState, bool isPersistent = false)
{
var Browser = Request.Browser + Request.Browser.Version;
var claims = new List<Claim>
{
// create required claims
new Claim(ClaimTypes.NameIdentifier, appUserState.UserId),
new Claim(ClaimTypes.Name, appUserState.Name),
new Claim(ClaimTypes.Role, appUserState.RoleName),
new Claim(ClaimTypes.UserData, Browser.GetHashCode().ToString()),
// User State Info
new Claim("userState", appUserState.ToString())
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
//Dictionary = { { "RememberMe", isPersistent ? "true" : "false" } },
ExpiresUtc = isPersistent ? DateTime.UtcNow.AddHours(3) : DateTime.UtcNow.AddMinutes(20)
}, identity);
}
I'm expecting that cookie should be alive for 3 hours, but it expires after less than 15 minutes.
It works as expected on local, but this happens only when i deploy to IIS.
Should I set asp.net session timeout to be same as expiration timeout?
Should I include any other IIS configuration?

After long research, I found that I have to add the following line to my web.config file
<system.web>
<sessionState mode="StateServer" timeout="1200" cookieless="false" />
</system.web>

Related

Force claim on user

I integrated with and old Active Directive through LDAP, where some users are to be looked up.
If I find the user, it should have access - no additional requirements.
I have a specific page, which I have placed a restriction on, so that only user from MyGroup can see. When I go to the site, I get redirected to my Login-page and get a nice ReturnUrl, but I get a 401 back for the page.
EPiServer refuses to let me in, although I can see I actual get my user.
How can I tell EPiServer, that a given user is allowed access?
I created my own LoginController, which calls a method like this:
public AuthenticationResult SignInAsLdapUser(string username, string password)
{
var principalContext = new PrincipalContext(/*...*/);
isAuthenticated = principalContext.ValidateCredentials(username, password);
if (!isAuthenticated)
return;
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, username);
//Attempt to force additional claims on user
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie, ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim(ClaimTypes.Role, "MyGroup")); //Group I am trying to add
HttpContext.Current.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.Current.GetOwinContext().Authentication.SignIn(new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.Now.AddMinutes(30),
AllowRefresh = true
}, identity);
return new AuthenticationResult();
}
in my web.config I specified the group MyGroup:
<virtualRoles addClaims="true">
<providers>
...
<add name="CmsEditors" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="MyGroup, ..." mode="Any"/>
...
</providers>
</virtualRoles>
In my startup.cs I specified the built-in Identity and cookies:
public void Configuration(IAppBuilder app)
{
app.AddCmsAspNetIdentity<ApplicationUser>(); //OWIN from EPiServer
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationMode = AuthenticationMode.Active,
CookieSecure = CookieSecureOption.Always,
LoginPath = new PathString("/Login"),
CookieName = "MyCookie",
ExpireTimeSpan = TimeSpan.FromDays(7),
ReturnUrlParameter = "ReturnUrl",
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator
.OnValidateIdentity<ApplicationUserManager<ApplicationUser>, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user)),
OnApplyRedirect = context => context.Response.Redirect(context.RedirectUri),
OnResponseSignIn = context => context.Response.Redirect(GetReturnUrl()),
}
});
app.UseStageMarker(PipelineStage.Authenticate);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
}

Redirect back to the ASP.NET Mvc Client after Sign-out from IdentityServer

I want to redirect back to my client after sign-out from local, then the IS4; My AspNetCore Mvc client works correctly and redirect back to the client after sign-out, but the AspNet Mvc (not Core) it doesn't.
here is my Startup.Configuration method:
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
SignInAsAuthenticationType = "Cookies",
Authority = "https://localhost:5000",
UseTokenLifetime = false,
// RedeemCode = true,
ClientId = "aspNet_client",
ClientSecret = "secret",
RedirectUri = "https://localhost:44343/sigin-oidc",
PostLogoutRedirectUri = "https://localhost:44343/signout-callback-oidc",
SaveTokens = true,
ResponseType = "code id_token",
Scope = "openid profile offline_access",
TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = JwtClaimTypes.PreferredUserName,
RoleClaimType = JwtClaimTypes.Role,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = onAuthenticationFailed,
MessageReceived = onMessageReceived,
// AuthorizationCodeReceived = onAuthorizationCodeReceived
}
});
}
I used this method to sign-out:
public ActionResult SignOut()
{
Request.GetOwinContext().Authentication.SignOut();
return Redirect("/");
}
I used this method too:
public ActionResult SignOut()
{
System.Web.HttpContext.Current.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties
{
RedirectUri = "https://localhost:44343"
},
CookieAuthenticationDefaults.AuthenticationType,
OpenIdConnectAuthenticationDefaults.AuthenticationType
);
//"Cookies", "OpenIdConnect"
}
But not worked. So my question is:
How to automatic redirect back to my AspNetMvc Client after sign-out?
This was an error reported long time ago on IdentityServer3. It got fixed here by setting IdTokenHint on logout. In this case as we use IdentityServer4, we can implement similar fix manually on ASP.NET MVC app. Here is changes need to make:
on IdentityServer project set PostLogoutRedirectUris for the client:
new Client
{
ClientId = "aspNet_client",
//All other settings ...
PostLogoutRedirectUris = { "http://localhost:44343" },
},
On ASP.NET mvc application, set OpenIdConnectAuthenticationOptions - PostLogoutRedirectUri to the same value as step 1
Change Notifications - SecurityTokenValidated and RedirectToIdentityProvider to set IdTokenHint on logout
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
// other settings...
PostLogoutRedirectUri = "http://localhost:44343",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var id_token_claim = n.OwinContext.Authentication.User.Claims.FirstOrDefault(x => x.Type == "id_token");
if (id_token_claim != null)
{
n.ProtocolMessage.IdTokenHint = id_token_claim.Value;
}
}
return Task.FromResult(0);
}
}
});
If you want to redirect automatically set AccountOptions - AutomaticRedirectAfterSignOut to true on IdentityServer, default value is false.
Implemented it myself here

Asp.Net Mvc "Remember Me" not working on server

I am developing web application with asp.net mvc.
It's seems to work in local, but in a shared hosting, It last for about 10 minutes then logout.
Here is the code:
AccountController.cs
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var context = Request.GetOwinContext();
var authenticationManager = context.Authentication;
authenticationManager.SignIn(new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddDays(90), IsPersistent = true }, identity);
return RedirectToAction("Index", "Dashboard");
Startup.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "social",
CookieSecure = CookieSecureOption.Never
});
}
Solving my problem by adding machine key, hope helps someone

Social Signout not working in conjunction with OpenIdConnect

I created an MVC Web Application with OpenIdConnect authentication (for Azure Authentication) and Authentication providers for Google, Facebook and Microsoft Account.
The Configuration in StartupAuth looks like this:
public void ConfigureAuth(IAppBuilder app)
{
if (Config.TaskboardUserSource == Config.DirectoryService.AzureAD)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = new TimeSpan(6, 0, 0),
SlidingExpiration = true,
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Home/Index"),
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));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = Config.ClientId,
Authority = string.Format("{0}common", Config.AadInstance),
UseTokenLifetime = false,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
SecurityTokenValidated = (context) =>
{
return Task.FromResult(0);
},
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(Config.ClientId, Config.AppKey);
string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}{1}", Config.AadInstance, tenantID), new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code,
new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),
credential,
Config.GraphResourceID).Result;
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
// This ensures that the address used for sign in and sign out is picked up dynamically from the request
// this allows you to deploy your app (to Azure Web Sites, for example)without having to change settings
// Remember that the base URL of the address used here must be provisioned in Azure AD beforehand.
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl + "/";
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.OwinContext.Response.Redirect("/Home/Index");
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
}
});
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = Config.FBAppId,
AppSecret = Config.FBAppSecret,
UserInformationEndpoint = Config.FBUserInformationEndpoint
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = Config.GoogleClientId,
ClientSecret = Config.GoogleClientSecret
});
var microsoftOptions = new MicrosoftAccountAuthenticationOptions()
{
ClientId = Config.MSAppId,
ClientSecret = Config.MSAppSecret,
};
microsoftOptions.Scope.Add("wl.basic");
microsoftOptions.Scope.Add("wl.emails");
app.UseMicrosoftAccountAuthentication(microsoftOptions);
}
}
All authentication options work fine.
When I want to signout, the only signout working is OpenIdConnect Signout.
For all other authentication providers, the cookie is still available and just by clicking the "Logon" Button the secured pages are shown without asking for a password.
My Signout looks like this:
public void SignOut()
{
string callbackUrl = Url.Action("SignOutCallback", "Account", routeValues: null, protocol: Request.Url.Scheme);
HttpContext.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
HttpContext.GetOwinContext()
.Authentication.GetAuthenticationTypes()
.Select(o => o.AuthenticationType).ToArray());
HttpContext.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
CookieAuthenticationDefaults.AuthenticationType);
}
How can I make sure the user is signed out and gets redirected to the start page?
After I inserted a switch case statement in my signout code to do the signout for every logonprovider it finally works. Here is my code:
public async Task<ActionResult> SignOut()
{
var currentUser = await UserService.CurrentUser();
if (currentUser != null)
{
var redirectUrl = Request.GetBaseUrl();
var loginProviders = new string[] {
"Google",
"TwoFactorRememberBrowser",
"TwoFactorCookie",
"ExternalCookie",
"ApplicationCookie"
};
switch (currentUser.LoginProvider)
{
case LogonProvider.FacebookProviderKey:
{
loginProviders = new string[] {
"Facebook",
"TwoFactorRememberBrowser",
"TwoFactorCookie",
"ExternalCookie",
"ApplicationCookie" };
break;
}
case LogonProvider.GoogleProviderKey:
{
loginProviders = new string[] {
"Google",
"TwoFactorRememberBrowser",
"TwoFactorCookie",
"ExternalCookie",
"ApplicationCookie" };
//return new RedirectResult($"https://www.google.com/accounts/Logout");
break;
}
case LogonProvider.MicrosoftProviderKey:
{
loginProviders = new string[] {
"Microsoft",
"TwoFactorRememberBrowser",
"TwoFactorCookie",
"ExternalCookie",
"ApplicationCookie" };
break;
}
default:
{
loginProviders = new string[] {
"Office365",
"TwoFactorRememberBrowser",
"TwoFactorCookie",
"ExternalCookie",
"ApplicationCookie" };
break;
}
}
HttpContext.GetOwinContext().Authentication.SignOut(new AuthenticationProperties { RedirectUri = redirectUrl }, loginProviders);
}
return RedirectToAction("Index", "Home");
}

Why is cookie's expiration date is 'Session' when using Owin

My web application is MVC5. I'm calling an url of IdentityServer4 application to authenticate user when logging in.
Here is the method ConfigureAuth of Startup class in my application
public void ConfigureAuth(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
var authority = LayeredConfiguration.GetValue("HydraInsuranceWeb-UserManagement-Authority");
var redirectUri = LayeredConfiguration.GetValue("HydraInsuranceWeb-UserManagement-RedirectUri");
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = "Cookies",
SlidingExpiration = false,
ExpireTimeSpan = System.TimeSpan.FromMinutes(2),
CookieName = "MyTestCookie"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = authority,
ClientId = AuthConstants.InsuranceWebClientId,
Scope = "openid profile user.management hydra.eventhistory.api",
RedirectUri = redirectUri,
ResponseType = "code id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
try
{
var transformedHydraIdentity = new HydraIdentityBuilder(n.AuthenticationTicket.Identity)
.AllowSecurityAdmin()
.IncludeRoleProfiles()
.IncludeIdToken(n.ProtocolMessage.IdToken)
.IncludeStandardClaims()
.Build();
n.AuthenticationTicket = new Microsoft.Owin.Security.AuthenticationTicket(
transformedHydraIdentity,
n.AuthenticationTicket.Properties);
}
catch (Exception ex)
{
n.HandleResponse();
n.Response.Redirect("/Error/NoAuthorization");
DiagnosticService.Writer.AddError("Authentication Error", ex);
}
return Task.FromResult(0);
},
}
});
}
After logging in, the cookie's expiration is always "Session", not the current time plus 2 minutes.
But my expectation is the cookie's expiration is a specific datetime, it should be current time plus 2 minutes. If user doesn't operate in 2 minutes, jump to the login page.
Has anyone known this issue? Please tell me how to investigate or debug to know why cookie's expiration is changed.
And there are 2 cookies: .AspNet.Cookies and MyTestCookie. Which cookie is used to authenticate user?
You need to set IsPersistent to True when signing in.
AuthenticationManager.SignIn(new AuthenticationProperties{ IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30)}, userIdentity);

Resources