The scenario you are describing should be working correctly unless the logout page is not actually deleting the forms authentication cookie. There are several ways to end the forms authentication session:
//I have seen instances where this does not work.
FormsAuthentication.SignOut()
//I have not seen this code fail before.
Dim cookie As HttpCookie = FormsAuthentication.GetAuthCookie( _
HttpContext.Current.User.Identity.Name, False)
cookie.Expires = Date.Now.AddDays(-1)
Response.Clear()
Response.AppendCookie(cookie)
Response.Redirect(FormsAuthentication.LoginUrl)
Also if you are using a role manager which stores in a cookie remember to call Roles.DeleteCookie().
Edit: In response to the updated question.
The Response.Redirect method does not return a header with a new URL referrer because the spec says that only client initiated requests should contain a referrer header. Here is the Response.Redirect code which you can see does not change the referrer header:
Public Sub Redirect(ByVal url As String, ByVal endResponse As Boolean)
If (url Is Nothing) Then
Throw New ArgumentNullException("url")
End If
If (url.IndexOf(ChrW(10)) >= 0) Then
Throw New ArgumentException(SR.GetString("Cannot_redirect_to_newline"))
End If
If Me._headersWritten Then
Throw New HttpException(SR.GetString("Cannot_redirect_after_headers_sent"))
End If
Dim handler As Page = TryCast(Me._context.Handler,Page)
If ((Not handler Is Nothing) AndAlso handler.IsCallback) Then
Throw New ApplicationException(SR.GetString("Redirect_not_allowed_in_callback"))
End If
url = Me.ApplyRedirectQueryStringIfRequired(url)
url = Me.ApplyAppPathModifier(url)
url = Me.ConvertToFullyQualifiedRedirectUrlIfRequired(url)
url = Me.UrlEncodeRedirect(url)
Me.Clear
If (((Not handler Is Nothing) AndAlso handler.IsPostBack) AndAlso (handler.SmartNavigation AndAlso (Me.Request.Item("__smartNavPostBack") = "true"))) Then
Me.Write("<BODY><ASP_SMARTNAV_RDIR url=""")
Me.Write(HttpUtility.HtmlEncode(url))
Me.Write("""></ASP_SMARTNAV_RDIR>")
Me.Write("</BODY>")
Else
Me.StatusCode = &H12E
Me.RedirectLocation = url
If ((url.StartsWith("http:", StringComparison.OrdinalIgnoreCase) OrElse url.StartsWith("https:", StringComparison.OrdinalIgnoreCase)) OrElse ((url.StartsWith("ftp:", StringComparison.OrdinalIgnoreCase) OrElse url.StartsWith("file:", StringComparison.OrdinalIgnoreCase)) OrElse url.StartsWith("news:", StringComparison.OrdinalIgnoreCase))) Then
url = HttpUtility.HtmlAttributeEncode(url)
Else
url = HttpUtility.HtmlAttributeEncode(HttpUtility.UrlEncode(url))
End If
Me.Write("<html><head><title>Object moved</title></head><body>" & ChrW(13) & ChrW(10))
Me.Write(("<h2>Object moved to <a href=""" & url & """>here</a>.</h2>" & ChrW(13) & ChrW(10)))
Me.Write("</body></html>" & ChrW(13) & ChrW(10))
End If
Me._isRequestBeingRedirected = True
If endResponse Then
Me.End
End If
End Sub
You can use reflector to follow the other methods but I don't see one which changes any header.
Q1: Forms authentication uses machineKey to encrypt the cookie. Since its value is constant in machine.config ASP.NET is able to decrypt cookies encrypted with the same key.
The cookies are encrypted with the same key but this key is known only to the server, which means that the user cannot tamper with the data of the cookie and thus cannot impersonate another user, so it is not a security risk to use the same private key to encrypt cookies.
Q2: The ticket contains the following information: the username and a date which is used to determine if it is valid (if sliding expiration is set, ASP.NET could rewrite the cookie as it checks its validity on every request). If the cookie is sent by the client and when it gets decrypted it is still valid, ASP.NET assumes that the client is authenticated.
Best Answer
Oddly enough, I just researched this this week.
It turns out that the FormsAuthenticationModule does do the actual redirect in EndRequest event handler. However, it doesn't decide that the redirect should happen. It does the redirect if the response status code is 401 (Unauthorized).
The UrlAuthorizationModule is the place where the decision is made (as mentioned in another answer), but all it does is indicate that the request is not authorized by setting the response status code to 401.
So, it is actually the two modules in coordination that make the redirect to the login page happen.