Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IdentityServer Login with external provider not working for long login_hint or acr_values

In my OpenIdConnectAuthenticationOptions I set the OpenIdConnectAuthenticationNotifications RedirectToIdentityProvider

It looks like this:

RedirectToIdentityProvider = n =>
{
    if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.AuthenticationRequest)
    {
        n.ProtocolMessage.LoginHint = "LoginHint";
        n.ProtocolMessage.AcrValues = "idp:CustomIdProvider";
    }
    return Task.FromResult(0);
}

In this example I am able to receive the LoginHint and everything works fine.

Now if I set the LoginHint to something that is about 1000 characters long (The same happens for AcrValues), the IdentityServer shows an error message:

There is an error determining which application you are signing into. Return to the application and try again.

and the logs show this message:

No cookie matching signin id found

This only happens when the LoginHint (or AcrValues) reach a certain size

It seems there is a problem when storing the cookies or reading the cookies maybe they are to big

What I've already tried/configured:

Web Config for both, client and server (according to this answer all these values should be high enough, I will reduce them to appropriate values when it's working):

<system.web>
    <httpRuntime targetFramework="4.6.1" maxUrlLength="109990" maxQueryStringLength="100000" maxRequestLength="256000" />
</system.web>
<!--...-->
<requestFiltering>
    <requestLimits maxQueryString="100000" maxAllowedContentLength="1073741824" />
</requestFiltering>

InputLengthRestrictions in the IdentityServerOptions(again the values should be sufficient):

InputLengthRestrictions = new InputLengthRestrictions
{
    UserName = 51200,
    AcrValues = 51200,
    LoginHint = 51200
}

This is a follow up question for this one: Send a custom parameter to an external identity provider

EDIT:

More information about my structure:

My client receives a token as queryparameter which can be very long(about 900 chars).
The client now redirects to the IdentityServer using following options for: app.UseOpenIdConnectAuthentication(options);

Clients Startup.cs:

RedirectToIdentityProvider = n =>
{
    if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.AuthenticationRequest)
    {
        var token = n.Request.Query.Get("token");
        if (token != null)
        {
            n.ProtocolMessage.Parameters.Add("token", token);
            n.ProtocolMessage.AcrValues = "idp:CustomIdP";
        }

    }
    return Task.FromResult(0);
}

The rest of the options is pretty basic

On my IdentityServer I configure the AuthenticationOptions' IdentityProviders-Property as you can see in this excerpt of my IdServer Configuration and I also set the InputLengthRestrictions to a high value, just to be safe:

IdentityServer Startup.cs:

IdentityServerOptions options = new IdentityServerOptions
{
    InputLengthRestrictions = new InputLengthRestrictions
    {
        RedirectUri = 51200,
        AcrValues = 51200,
        LoginHint = 51200
    },
    AuthenticationOptions = new AuthenticationOptions {

        CookieOptions = new IdentityServer3.Core.Configuration.CookieOptions
        {
            SessionStoreProvider = new SessionStoreProvider()
        },
        IdentityProviders = ConfigureIdentityProviders,
    }
};
idsrvApp.UseIdentityServer(options);

Then I configure my IdentityProviders, my IdentityProvider uses the Token from the parameter specified in the Clients Startup.cs This works fine for a short token, everything is called as it should be.

But if the token is to long it doesn't even get that far. My guess is that the root of the problem lies in the OpenIdConnectAuthenticationHandler

EDIT 2

Why the limit is hit so fast:

Apparently my token is added twice to the request to the IdentityServer.

Because of this reason the limit for the cookie is reached pretty fast.

Clients Startup.cs:

RedirectToIdentityProvider = n =>
{
    if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.AuthenticationRequest)
    {
        var token = n.Request.Query.Get("token");
        if (token != null)
        {
            n.ProtocolMessage.Parameters.Add("token", token);
            n.ProtocolMessage.AcrValues = "idp:CustomIdP";
        }

    }
    return Task.FromResult(0);
}

Here I take the token from the QueryString. But what I missed here is, the n.ProtocolMessage contains already the RequestUri as State parameter which contains the token. So the token is sent twice to the IdentityServer. If I remove the token from the state-Parameter (which is the right thing to do, as I don't need it on redirecting back) and add it as AcrValue it does send it to the IdentityServer as expected.

But still the question remains.

What if the Token is really long?

like image 960
Florian K Avatar asked May 14 '18 13:05

Florian K


1 Answers

I can't be sure, but, this sounds like it may be a max cookie size problem.
Cookies can only store 4096 bytes in most browsers, and if cookies are stored in UTF-32 for example, then 1024 characters would take up all of that space and your cookie would be truncated.

You may want to try overriding one of the CookieOptions properties in the AuthenticationOptions.

In the CookieOptions class you can provide an IAuthenticationSessionStoreProvider. According to the comment on the property it may be the solution you are looking for, at the very least you may be able to debug what is going wrong.

/// <summary>
///   An optional container in which to store the identity across requests.
///   When used, only a session identifier is sent
///     to the client. This can be used to mitigate potential problems 
///     with very large identities.
/// </summary>
public IAuthenticationSessionStoreProvider SessionStoreProvider { get; set; }

There is no default implementation for IAuthenticationSessionStoreProvider but you can look at how it is used inside AuthenticationSessionStoreWrapper

It is wrapped up inside an AuthenticationSessionStoreWrapper if you add a provider:

static IAuthenticationSessionStore GetSessionStore(IAuthenticationSessionStoreProvider provider)
{
    return provider != null ? new AuthenticationSessionStoreWrapper(provider) : null;
}
like image 53
Kyle B Avatar answered Nov 13 '22 09:11

Kyle B