I am trying to implement IdP-initiated Sso login on my angular app and IdentityServer4 environment. My setup is as follows Angular app is runngin on https://localhost:4200 IdentityServer4 is running on http://localhost:7000 I use Sustainsys.Saml2 library to implement ServiceProvider part. Here are my codes IdentitServer4 services.AddAuthentication() .AddCookie() .AddSaml2("Saml2", "Login with SSO", opt => { opt.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; opt.SignOutScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; opt.SPOptions.EntityId = new EntityId("http://localhost:4200"); opt.SPOptions.ReturnUrl = new Uri("https://localhost:7258/home/IdpInitiatedRedirect?idp=Saml2"); // Idp using a custom query string param on the Acs URL. opt.IdentityProviders.Add(new IdentityProvider(new EntityId("https://stubidp.sustainsys.com/1b1f27d2-1d59-46aa-8756-3597337da1fe/Metadata"), opt.SPOptions) { LoadMetadata = true, AllowUnsolicitedAuthnResponse = true, }); opt.Notifications.AcsCommandResultCreated = AcsCommandResultCreated; }); private static void AcsCommandResultCreated(CommandResult commandResult, Saml2Response saml2Response) { var httpContext = _httpContextAccessor.HttpContext; var target = httpContext.Request.Query["target"].SingleOrDefault(); if (!string.IsNullOrEmpty(target)) { var targetUri = new Uri(target, UriKind.Relative); if (target.StartsWith("//")) { throw new InvalidOperationException("Protocol relative URLs are not allowed."); } commandResult.Location = targetUri; } } ExternalController.cs in IdentityServer4 public async Task<IActionResult> SustainLogin() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { //getting user data from my database string email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value; Member _member = await _memberDal.GetAsync(x => x.Email == email); if (_member == null) { user = null; } else { user = new TestUser { SubjectId = _member.Email, Username = _member.FirstName + " " + _member.LastName, }; } } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List<Claim>(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } I used the below samples https://github.com/Sustainsys/Saml2/blob/v1/docs/identity-server-3-okta.rst https://github.com/Sustainsys/AspNetcoreIdpInitiated/blob/main/AspNetCoreIdpInitiated/Startup.cs The problem is that the angular app does not get a token for the first time. when I click the My Account link it redirects IdentityServer again to check if I logged in successfully. I realized IdentityServer logged me in and successfully redirected me to the angular app. I think returnUrl was not generated correctly but I am not sure how to generate that. Any help would be appreciated. Continue reading...