1. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

IdP initialted SSO does not get token first time

Discussão em 'Angular' iniciado por ypbr, Outubro 1, 2024 às 07:12.

  1. ypbr

    ypbr Guest

    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...

Compartilhe esta Página