Unanswered wsignin1.0 passive redirect - parameters on querystring?

  • Wednesday, July 18, 2012 2:49 PM
     
      Has Code

    The WS-Federation passive requestor profile states that "Parameterization is extensible so that cooperating parties can exchange additional information in parameters based on agreements or policy.".  The Microsoft.IdentityModel.Protocols.WSFederation.SignInRequestMessage class supports a Parameters dictionary, and does include the parameters on the querystring when WriteQueryString() is used.

    However, when a signin request is sent to ADFS via GET (passive redireciton), and the WHR parameter is specified, ADFS drops all querystring parameters except wauth when forwarding the signin request to another STS (the home realm determined from WHR).

    To solve this problem for my solution, I have added an HttpModule to adfs/ls to preserve the querystring parameters when a wsignin1.0 request is forwarded to another STS.  I am curious to know if others have faced this same requirement, and how they solved it?  I have included my HttpModule code for reference:

     public class PreserveQueryStringModule : IHttpModule
        {
            private const string qsKey = "originalQS";
            private string[] exclude = new string[] 
            { 
                WSFederationConstants.Parameters.Action,
                WSFederationConstants.Parameters.Realm,
                WSFederationConstants.Parameters.Reply,
                WSFederationConstants.Parameters.Policy,
                WSFederationConstants.Parameters.Context,
                WSFederationConstants.Parameters.CurrentTime,
                WSFederationConstants.Parameters.ResultPtr,
                WSFederationConstants.Parameters.Attribute,
                WSFederationConstants.Parameters.AttributePtr,
                WSFederationConstants.Parameters.Encoding,
                WSFederationConstants.Parameters.Federation,
                WSFederationConstants.Parameters.Freshness,
                WSFederationConstants.Parameters.HomeRealm,
                WSFederationConstants.Parameters.Pseudonym,
                WSFederationConstants.Parameters.PseudonymPtr,
                WSFederationConstants.Parameters.Request,
                WSFederationConstants.Parameters.RequestPtr,
                WSFederationConstants.Parameters.Result,
                WSFederationConstants.Parameters.ResultPtr
            };
    
            public void Init(HttpApplication context)
            {
                context.BeginRequest += new EventHandler(context_BeginRequest);
                context.EndRequest += new EventHandler(context_EndRequest);
            }
    
            void context_BeginRequest(object sender, EventArgs e)
            {
                HttpApplication app = (HttpApplication)sender;
                if (app.Context.Request.QueryString[WSFederationConstants.Parameters.Action] 
                    == WSFederationConstants.Actions.SignIn)
                {
                    app.Context.Items[qsKey] = app.Context.Request.QueryString.AllKeys
                        .Where(x => !exclude.Contains(x))
                        .Select(x => new { key = x, value = app.Context.Request.QueryString[x] })
                        .ToDictionary(x => x.key, x => x.value);
                }
            }
    
            void context_EndRequest(object sender, EventArgs e)
            {
                HttpApplication app = (HttpApplication)sender;
    
                if (app.Response.StatusCode == 302 && app.Context.Items[qsKey] != null)
                {
                    Uri redirectUri = null;
    
                    if (Uri.TryCreate(app.Response.RedirectLocation, UriKind.Absolute, out redirectUri))
                    {
                        var qs = HttpUtility.ParseQueryString(redirectUri.Query);
    
                        if (qs[WSFederationConstants.Parameters.Action] == WSFederationConstants.Actions.SignIn)
                        {
                            foreach (var kvp in (Dictionary<string, string>)app.Context.Items[qsKey])
                            {
                                if (qs[kvp.Key] == null)
                                {
                                    qs.Add(kvp.Key, kvp.Value);
                                }
                            }
    
                            app.Response.RedirectLocation = String.Format("{0}://{1}{2}?{3}", redirectUri.Scheme, redirectUri.Authority, redirectUri.AbsolutePath, qs);
                        }
                    }
                }
    
            }
    
            public void Dispose() { }
        }

    To shed a little more light on the requirement - I have a use case where some contextual parameters (for white-label branding) must be passed to the STS that authenticates the user from the originating RP, to present the correct white-label branding UI.  I could have my RP's go directly to such an STS with the parameters on the querystring; however if you imagine my federation solution as a hub and spoke model, I want ADFS to be the hub that all parties trust, using the WHR parameter where needed to direct signin requests to the proper STS.

    Without my HttpModule, the only possibility I see is to add a full claims provider trust in ADFS for each variant of my white-label STS.  I cannot assign multiple identifiers to a single claims provider trust, and I cannot assign multiple passive endpoints to a single trust.  So doing this without the HttpModule would require unnecessary maintenance of many claims provider trusts for a single parameterized STS.

    I am curious to know if anyone has faced a similar problem and how they solved it.

All Replies