Sustainsys / Saml2

Saml2 Authentication services for ASP.NET
Other
961 stars 602 forks source link

How to pass a custom parameter and retrieve it when returning from IDP #1466

Closed qudooschaudhry closed 3 weeks ago

qudooschaudhry commented 3 months ago

I have inherited some code that uses Sustainsys.Saml2.Mvc 2.92 to redirect to OKTA for login. I need to pass an Id before going to OKTA and need to retrieve this Id after a successful login. I have been trying to find out how to do this but cannot find a definitive answer. Any way I can achieve this?

Thanks!

explunit commented 3 months ago

I think RelayState is the concept you are looking for: https://stackoverflow.com/q/34350160/151212

https://developer.okta.com/docs/concepts/saml/#understand-sp-initiated-sign-in-flow

qudooschaudhry commented 3 months ago

Thanks!

Is it just passed as a QueryString?

I updated my redirect to

return RedirectToAction("SignIn", "Saml2", new {idp=idPEntityId, RelayState=ssoRequestId})

In the network tab, I see the relaystate parameter being sent to OKTA. However, it is not coming back. I wonder if I need to enable something in OKTA admin to pass it back.

qudooschaudhry commented 3 months ago

OK, I see that the relaystate parameter is being posted back along side the SAML response the /acs endpoint. However after that I am not sure what the /acs endpoint does with it? How do I retrieve it, I see it nowhere in cookies or any other information when acs redirects to my url.

qudooschaudhry commented 2 months ago

@AndersAbel I have looked at the SAML2 code for the v2 branch and I think I spot an issue in the AcsCommand.

In the controller at the /acs end point It calls result.ApplyCookies which in turn looks for this

if (!string.IsNullOrEmpty(commandResult.SetCookieName))

However, I do not see the SetCookieName being set anywhere in the AcsCommand. I believe this in turn does not create the relay state cookie.

Do you think this is an issue? I can create a PR if you think it's an issue.

qudooschaudhry commented 2 months ago

Perhaps this is by design as I see it sets another cookie instead

result.ClearCookieName = StoredRequestState.CookieNameBase + unbindResult.RelayState;

and anything in the ClearCookieName is being expired in the ApplyCookies method.

if the value that my application passed to the IDP gets passed to IDP as relaystate but then it gets cleared on the /acs end point, how am I supposed to retrieve it back?

qudooschaudhry commented 2 months ago

ok digging further into this it seems that I need relayStateUsedAsReturnUrl="true" on my config setting. I tried this and was expecting that perhaps it gets appended as a relayState query string to my return url, however that did not happen either. Again looking at the AcsCommand --> GetLocation, it seems that if there is a storedRequestState, then it will not do anything with the relayStateUsedAsReturnUrl setting.

if (storedRequestState != null)
{
    return storedRequestState.ReturnUrl ?? options.SPOptions.ReturnUrl;

}
else
{ //When IDP-Initiated

    if (identityProvider.RelayStateUsedAsReturnUrl && !string.IsNullOrWhiteSpace(relayState))
    {
        if (!PathHelper.IsLocalWebUrl(relayState))
        {
            if (!options.Notifications.ValidateAbsoluteReturnUrl(relayState))
            {
                throw new InvalidOperationException("Return Url must be a relative Url.");
            }
        }
        return new Uri(relayState, UriKind.RelativeOrAbsolute);
    }
}

return options.SPOptions.ReturnUrl;
AndersAbel commented 2 months ago

When using the MVC package you would have to implement the AcsCommandResultCreated notification to retrieve the state. The SessionAuthenticationModule used by the MVC package doesn't support persisting the additional state to the session cookie.

qudooschaudhry commented 1 month ago

I added the notification

    .AcsCommandResultCreated = (commandResult, response) =>
{
    var relayState = commandResult.RelayState;
};

it does hit this when I come back to /acs however, commandresult.RelayState is empty.

AndersAbel commented 1 month ago

It's been a long time since I wrote or used the Mvc package, so I went back and read up on the code.

The short answer is that custom relaystate is not supported with the Mvc package. The relaystate you see being used is created for internal book-keeping. For Mvc applications you can use the Own package instead.

Another option is to create your own SignIn implementation based on the existing one in https://github.com/Sustainsys/Saml2/blob/3508193a1919d7fdefec5c5da319f4658f3f7d0d/Sustainsys.Saml2.Mvc/Saml2Controller.cs#L43. The command object returned from the CommandFactory is a SignInCommand. If you cast it to that, you can access another overload of the Run method that allows you to add custom relaystate. That would then be available in AcsCommandResultCreated