Identity server 4 post login redirect not working in chrome only
Solution 1
I had issues with chrome and edge recently but a few months ago was only chrome. So for me with .Net Core 3 and IdentityServer4 version 3.1.2 started working by adding the following code to startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ...)
{
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Lax
});
Note: Make sure that you add this policy to the beginning of the Configure method not end in the startup.cs otherwise is not working.
Solution 2
I had a similar issue with IdentityServer4 on .NET Core 2.2. Your problem might be connected with those breaking changes in new browsers versions like Chrome or Firefox:
For me the working solution was to turn off the SameSite configuration for cookies at all. Such possibility for .NET Core 2.2 is described here:
https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
(if your solution is on .NET Core 3.1 then in code below, instead using (SameSiteMode)(-1) you should use SameSiteMode.Unspecified)
FIX: in Startup.cs file in ConfigureServices method, right after IdentityServerBuilder is created...
var builder = services.AddIdentityServer(options =>
{....});
...I've added the following configuration change:
builder.Services.ConfigureExternalCookie(options => {
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1); //SameSiteMode.Unspecified in .NET Core 3.1
});
builder.Services.ConfigureApplicationCookie(options => {
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1); //SameSiteMode.Unspecified in .NET Core 3.1
});
Solution 3
You will get below console warring in Google Chrome and your Identity server failed to redirect to Client App for Chrome version 80.
A cookie associated with a resource at was set with SameSite=None but without Secure. It has been blocked, as Chrome now only delivers cookies marked SameSite=None if they are also marked Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5633521622188032.
To Fix this , you need to do changes mention in below link -
https://www.thinktecture.com/en/identity/samesite/prepare-your-identityserver/
NOTE : For .Net Core 2.2 , set SameSite = (SameSiteMode)(-1) , For .Net Core 3.0 or above , set SameSite = SameSiteMode.Unspecified
Also , for Chrome 80 version , add this extra condition -
if (userAgent.Contains("Chrome/8"))
{
return true;
}
Solution 4
Setting OpenidConnectionOptions worked for me
CorrelationCookie.SameSite = SameSiteMode.Lax;
NonceCookie.SameSite = SameSiteMode.Lax;
Pasha Guterman
Updated on June 14, 2022Comments
-
Pasha Guterman almost 2 years
i use identity server 4 let call it "auth-server" run on .net core 3.1. there is angular app request authentication after redirected to auth-server and provide credentials submiting the login it's not redirect back to client app. the issue is only in chrome browser (firefox & edge works fine) i can see the redirect request - Request-Url but it just go back to login page Client congig:
public static IEnumerable<Client> GetClients() { return new List<Client>(){ new Client() { RequireConsent =false, RequireClientSecret = false, ClientId = "takbull-clientapp-dev", ClientName = "Takbull Client", AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Profile, "takbull", "takbull.api" }, // where to redirect to after login RedirectUris = new List<string>() { "http://localhost:4200/auth-callback/", "http://localhost:4200/silent-refresh.html", }, //TODO: Add Production URL // where to redirect to after logout PostLogoutRedirectUris =new List<string>() { "http://localhost:4200" }, AllowedCorsOrigins = {"http://localhost:4200"}, AllowAccessTokensViaBrowser = true, AccessTokenLifetime = 3600, AlwaysIncludeUserClaimsInIdToken = true }, }; }
Login Code:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginInputModel model, string button) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); // the user clicked the "cancel" button if (button != "login") { if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (await _clientStore.IsPkceClientAsync(context.ClientId)) { // if the client is PKCE then we assume it's native, so this change in how to // return the response is for better UX for the end user. return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); } return Redirect(model.ReturnUrl); } else { // since we don't have a valid context, then we just go back to the home page return Redirect("~/"); } } if (ModelState.IsValid) { // validate username/password against in-memory store var ValidResp = await _users.ValidateCredentials(model.Username, model.Password); if (ValidResp.LogInStatus == LogInStatus.Success) { var user = _users.FindByUsername(model.Username); //await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username)); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Email, user.UserId.ToString(), user.Email)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.Now.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username //await HttpContext.SignInAsync(user.SubjectId, user.Username, props); // issue authentication cookie with subject ID and username await HttpContext.SignInAsync(user.UserId.ToString(), user.FirstName + " " + user.LastName, props, _users.GetClaims(user).ToArray()); if (context != null) { if (await _clientStore.IsPkceClientAsync(context.ClientId)) { // if the client is PKCE then we assume it's native, so this change in how to // return the response is for better UX for the end user. return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return Redirect(model.ReturnUrl); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return Redirect(model.ReturnUrl); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return Redirect("~/"); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, ValidResp.ResponseDescription)); ModelState.AddModelError(string.Empty, ValidResp.ResponseDescription); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); }
-
Roel over 3 yearsThe Chrome 80 extra condiction can be added to this code: github.com/IdentityServer/IdentityServer4/blob/… Here you call the extenstion method: github.com/IdentityServer/IdentityServer4/blob/main/src/…. But if Chrome hits version 90 or more it won't work anymore!