8

Here an interesting feature of ASP.NET FormsAuthentication explained in this SO answer: How do you pass an authenticated session between app domains

Quick summary; you can create two ASP.NET websites with the same encryption keys. WebsiteA can create a formsauth token, and redirect to WebsiteB with the token in the querystring (or POST body). Switch on EnableCrossAppRedirects in WebsiteB and ASP.NET detects the token and creates the formsauth cookie. In code:

FormsAuthentication.RedirectFromLoginPage("alice", true);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket("Alice", true, 30);
string encrypted = FormsAuthentication.Encrypt(ticket);
Response.Redirect("http://siteb.dev/Secure/WebForm1.aspx?" + FormsAuthentication.FormsCookieName + "=" + encrypted);

Sounds like a great feature, but where is it documented? I'd feel a bit uneasy using a undocumented feature.

Where I've looked - no mention of this feature in any of the MSDN reference. I thought maybe RedirectFromLoginPage would build a redirect like my code above, it doesn't.

Community
  • 1
  • 1
russau
  • 8,928
  • 6
  • 39
  • 49

1 Answers1

15

Having looked at reflector there is a (somewhat undocumented) feature of forms Authentication. When EnableCrossAppRedirects is enabled .NET will, in addition to looking for the auth cookie, attempt to extract the forms authentication "cookie" from either the form post or the query string. This code is embedded in the FormsAuthentication class in the ExtractTicketFromCookie method, where it can clearly been seen trying to find the authentication cookie in the request data.

if (FormsAuthentication.EnableCrossAppRedirects)
{
    text = context.Request.QueryString[name];
    if (text != null && text.Length > 1)
    {
        if (!cookielessTicket && FormsAuthentication.CookieMode == HttpCookieMode.AutoDetect)
        {
            cookielessTicket = CookielessHelperClass.UseCookieless(context, true, FormsAuthentication.CookieMode);
        }
        try
        {
            formsAuthenticationTicket = FormsAuthentication.Decrypt(text);
        }
        catch
        {
            flag2 = true;
        }
        if (formsAuthenticationTicket == null)
        {
            flag2 = true;
        }
    }
    if (formsAuthenticationTicket == null || formsAuthenticationTicket.Expired)
    {
        text = context.Request.Form[name];
        if (text != null && text.Length > 1)
        {
            if (!cookielessTicket && FormsAuthentication.CookieMode == HttpCookieMode.AutoDetect)
            {
                cookielessTicket = CookielessHelperClass.UseCookieless(context, true, FormsAuthentication.CookieMode);
            }
            try
            {
                formsAuthenticationTicket = FormsAuthentication.Decrypt(text);
            }
            catch
            {
                flag2 = true;
            }
            if (formsAuthenticationTicket == null)
            {
                flag2 = true;
            }
        }
    }
}

Therefore if you enable EnableCrossAppRedirects on both applications, then the first application is authorised to redirect to the external site, and the second application will automatically read in the authentication cookie from the request. You just need to engineer it so that the return login URL either posts the cookie data or sends it in the querystring. You also need to be sure that either the machine keys are synchronised, or that the cookie is encrypted using the external apps machine key (by the first app). It seems by default .NET will send the encrypted authentication cookie in the querystring for you and asume your machine keys are in sync (see MSDN quote below).

Here's some more info on MSDN .

If the CookiesSupported property is true, and either the ReturnUrl variable is within the current application or the EnableCrossAppRedirects property is true, then the RedirectFromLoginPage method issues an authentication ticket and places it in the default cookie using the SetAuthCookie method.

If CookiesSupported is false and the redirect path is to a URL in the current application, the ticket is issued as part of the redirect URL. If CookiesSupported is false, EnableCrossAppRedirects is true, and the redirect URL does not refer to a page within the current application, the RedirectFromLoginPage method issues an authentication ticket and places it in the QueryString property.

There is a big warning about the impact on security. EnableCrossAppRedirects is a security setting which prevents ASP.NET login controls from redirecting to an external return URL (another web application). With this setting enabled it can be exploited in some forms of attack - a user is sent to the official login page, but on login is redirected to a different application which they may believe is the same. This is why it's disabled by default.

One way to help mitigate this when enabling the feature is as follows:

To improve security when using cross-application redirects, you should override the RedirectFromLoginPage method to allow redirects only to approved Web sites.

You also need to ensure the redirect request is served over SSL to protect the "cookie" in transit, as anyone intercepting would be able to gain control of the account.

TheCodeKing
  • 19,064
  • 3
  • 47
  • 70
  • Sorry revised my answer, there is a hidden feature of .NET that will automatically pick up the authentication cookie form a form post or query string. I just learned something new! – TheCodeKing Aug 30 '11 at 16:42
  • Great answer! So I'm sort of subverting a feature of 'no cookies' auth. Re security hazards: I'm not using RedirectFromLoginPage, so I control where the user get redirected to. Redirect over SSL - the token on the POST/querystring is no less protected than the formsauth cookie, safest bet is to put the entire website over SSL. – russau Aug 30 '11 at 21:49
  • BTW - why the community wiki? will you still get the bounty when I award it? – russau Aug 30 '11 at 22:06
  • No idea why it says that, prob cause I revised it so many times? I'm still a noob to SO. – TheCodeKing Aug 30 '11 at 22:11
  • 1
    The code described by @TheCodeKing has been moved to FormsAuthenticationModule. – Andriy Volkov Jul 17 '12 at 21:24