New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State on URL is too long for Azure AD #407

Closed
rposener opened this Issue Oct 14, 2016 · 1 comment

Comments

Projects
None yet
1 participant
@rposener

rposener commented Oct 14, 2016

When using Azure AD as a federated provider, the state querystring is too long to return with the claims. It does seem to be an issue more on Azure's side, but is there an easy way to make the state shorter? this is the config I'm using in Startup.cs on the IdentityServer in QuickStart4_ExternalAuthentication. Google works fine, but adding this for Azure AD fails:

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { DisplayName = "Azure AD", SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, SignOutScheme = IdentityServerConstants.SignoutScheme, ClientId = "XXXXXXXXXXXXXXXXXXXXXXXXXXX", ClientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXX", Authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}{1}", "common", "/v2.0"), ResponseType = OpenIdConnectResponseType.IdToken, PostLogoutRedirectUri = "https://localhost:44326/", Events = new OpenIdConnectEvents { OnRemoteFailure = OnAuthenticationFailed, } });

the same config works fine in a plain MVC app. Also, if I remove the state from the URL, Azure AD finishes the auth and redirects me back without issue. But of course IdentityServer can't process it without a state parameter.

@rposener

This comment has been minimized.

rposener commented Oct 14, 2016

For anyone else finding this issue, I found the resolution from another user's fork of the repository. Basically, you need to cache the state, and provide a different State Format.

In Startup.cs do this:
` var dataProtectionProvider = app.ApplicationServices.GetRequiredService();
var distributedCache = app.ApplicationServices.GetRequiredService();

        var dataProtector = dataProtectionProvider.CreateProtector(
            typeof(OpenIdConnectMiddleware).FullName,
            typeof(string).FullName, schemeName,
            "v1");

        var dataFormat = new CachedPropertiesDataFormat(distributedCache, dataProtector);`

then in OpenIdConnectOptions set the property StateDataFormat = dataFormat

here is the code for the CachedPropertiesDataFormat class
`using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.Caching.Distributed;

namespace QuickstartIdentityServer.Authentication
{
///

/// Custom secure data format intended for use with OpenIdConnectMiddleware.
/// It is intended to reduce the size of the state parameter generated
/// by the default PropertiesDataFormat, which results in query strings
/// that are too long for Azure AD to handle.
/// </summary>


public class CachedPropertiesDataFormat
    : ISecureDataFormat<AuthenticationProperties>
{
    public const string CacheKeyPrefix = "CachedPropertiesData-";

    private readonly IDistributedCache _cache;
    private readonly IDataProtector _dataProtector;
    private readonly IDataSerializer<AuthenticationProperties> _serializer;

    public CachedPropertiesDataFormat(
        IDistributedCache cache,
        IDataProtector dataProtector)
        : this(cache, dataProtector, new PropertiesSerializer())
    {

    }

    public CachedPropertiesDataFormat(
        IDistributedCache cache,
        IDataProtector dataProtector,
        IDataSerializer<AuthenticationProperties> serializer)
    {
        _dataProtector = dataProtector;
        _cache = cache;
        _serializer = serializer;
    }

    public string Protect(AuthenticationProperties data)
    {
        return Protect(data, null);
    }

    public string Protect(AuthenticationProperties data, string purpose)
    {
        var key = Guid.NewGuid().ToString();
        var cacheKey = $"{CacheKeyPrefix}{key}";
        var serialized = _serializer.Serialize(data);

        // Rather than encrypt the full AuthenticationProperties
        // cache the data and encrypt the key that points to the data
        _cache.Set(cacheKey, serialized);

        return _dataProtector.Protect(key);
    }

    public AuthenticationProperties Unprotect(string protectedText)
    {
        return Unprotect(protectedText, null);
    }

    public AuthenticationProperties Unprotect(string protectedText, string purpose)
    {
        // Decrypt the key and retrieve the data from the cache.
        var key = _dataProtector.Unprotect(protectedText);
        var cacheKey = $"{CacheKeyPrefix}{key}";
        var serialized = _cache.Get(cacheKey);

        return _serializer.Deserialize(serialized);
    }

}

}`

@rposener rposener closed this Oct 14, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment