1

I am using WIF and a federated security model using ThinkTecture STS.

When I try to request the url: http://domain.com/#page, WIF is not redirecting to the correct page after authentication.

The ru param in wctx does not contain the correct path of /#path. Instead it ignores the hash and everything after it, so the ru param is just /. A normal url without a hash works fine.

Is there a workout around for this or am I formatting my url incorrectly?
Any suggestions?

Freak
  • 6,786
  • 5
  • 36
  • 54
peter.swallow
  • 905
  • 14
  • 38

4 Answers4

3

You can preserve the hash part by emitting JavaScript to perform the redirect, instead of redirecting immediately. The JavaScript code can access the hash part via window.location.hash, and use it to build the ru.

You need to configure the page to allow unauthenticated users (so that WIF passive authentication doesn't kick in). Then you can handle unauthenticated users in the page code.

You can hook the FederatedAuthentication.WSFederationAuthenticationModule.RedirectingToIdentityProvider event in the application start-up code (e.g. Global.asax.cs in Web Forms).

For example (Web Forms):

public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        FederatedAuthentication.WSFederationAuthenticationModule.RedirectingToIdentityProvider 
        += this.RedirectToIdentityProviderViaJavaScript;
    }

    const string RedirectHtml =
    @"<html>
        <head>
            <script type='text/javascript'>
                function authenticate(url, utcTimeString) {{
                    var ru = window.location.pathname + (window.location.hash || '');
                    var wctx = 'rm=0&id=passive&ru=' + encodeURIComponent(ru) + '&wtc=' + encodeURIComponent(utcTimeString);
                    url += '&wctx=' + encodeURIComponent(wctx);
                    window.location = url;
                }}
            </script>
        </head>
        <body onload=""authenticate('{0}', '{1}');"">
        </body>
    </html>";

    private void RedirectToIdentityProviderViaJavaScript(object sender, RedirectingToIdentityProviderEventArgs e)
    {
        var fam = FederatedAuthentication.WSFederationAuthenticationModule;
        var msg = new SignInRequestMessage(new Uri(fam.Issuer), fam.Realm);
        var stsUrl = msg.WriteQueryString();
        var utcTime = WebPageRoutines.EncodeUtcTimeString(DateTime.Now);
        var html = string.Format(RedirectHtml, WebPageRoutines.JavascriptEncode(stsUrl), WebPageRoutines.JavascriptEncode(utcTime));
        Response.ClearContent();
        Response.Write(html);
        Response.Status = "200 OK";
        Response.End();
    }
}

Be warned that you can't mix ? parameters with # parts with this approach. The ru survives the STS redirection (Thinktecture IdentityServer v2), but WIF seems to mess it up on the final redirect after the POST from the STS.

It will place the ? part after the # part.
http://www.somewebsite.com/page?param=1&other=2#hashbit
Becomes:
http://www.somewebsite.com/page#hashbit?param=1&other=2

Tom Mulgrew
  • 131
  • 1
  • 7
2

Better to use CreateSignInRequest to get all parameters from web.config. Guess it fixes the problem with the querystring to. Example using MVC

        const string redirectHtml =
            @"<!DOCTYPE html>
              <html>
                <head>
                    <meta charset='utf-8'>
                    <script type='text/javascript'>
                        function authenticate(url) {{
                            var ru = window.location.pathname + (window.location.hash || '');
                            window.location = url.replace('REPLACEWITHURL', encodeURIComponent(ru));
                        }}
                    </script>
                </head>
                <body onload=""authenticate('{0}');"">
                </body>
            </html>";

        var authenticationModule = FederatedAuthentication.WSFederationAuthenticationModule;
        var message = authenticationModule.CreateSignInRequest("passive", "REPLACEWITHURL", false);
        var stsUrl = message.WriteQueryString();
        var html = string.Format(redirectHtml, HttpUtility.JavaScriptStringEncode(stsUrl));
        filterContext.Result = new ContentResult { Content = html };
  • This works just great. I'm not in an MVC app so my solution was adapted. `filterContext.Result = new ContentResult { Content = html };` was reverted to the original solution: `Response.ClearContent(); Response.Write(html); Response.Status = "200 OK"; Response.End();` – Brandon Wittwer Jan 08 '16 at 22:05
  • It should be noted, in this solution "REPLACEWITHURL" is not an instruction to the developer to put a URL there. It's the string the event looks to replace when writing the redirect page. – Brandon Wittwer Jan 08 '16 at 22:09
1

It looks like its the browser that isn't sending the hash part of the url back to the server. I believe this is a HTTP standard, as the hash part was originally only intended for client side anchor tagging.

There are workarounds using ajax/javascript, but as I am using a simple GET request, it would appear impossible.

See these similar questions, which explain the problem...

How to get Url Hash (#) from server side

do browsers remove # in URL automatically?

Community
  • 1
  • 1
peter.swallow
  • 905
  • 14
  • 38
1

That's the whole point of hash fragments - that they don't end up on a server.

leastprivilege
  • 18,196
  • 1
  • 34
  • 50