I have an OWIN app that authenticates users with cookie authentication, and if that fails then it tries WS-Federated authentication. In other words, if the correct cookie isn't there in the initial request, then go to the STS and fetch the security token.
The problem I'm having is making sure the user's original URL request is fulfilled if federated authentication is used. Thanks to this post, I realized that the redirect URL from the STS back to the OWIN app contains the "original" URL in the wctx query parameter. The issue is that this value is set, as far as I can tell, using WsFederationAuthenticationOptions.Wtrealm
(perhaps Wreply
?). This is a problem because these values are set in the initial configuration. I don't want a hard coded value -- I just want the URL the user originally used (i.e. IOwinContext.Request.Uri
). The documentation for Wtrealm
and Wreply
does not help explain what the values should be.
I thought I could sneakily set Wtrealm
to the user's request URL before redirecting to the STS, but apparently it's already set by the time RedirectToIdentityProvider
notification is raised:
RedirectToIdentityProvider = context =>
{
context.Options.Wtrealm = context.Request.Uri.ToString();
}
Does anyone know what the correct approach is? Is there a way to make Wtrealm
in the initial configuration the user's request URL? Or is Wtrealm
not what I think it is, and I should be approaching this in a different way?
I believe I figured it out. Wtrealm
, as I suspected, isn't what's causing the issue; that is used to determine where the STS should reenter to finish the federated authentication, but there is another redirect URL that determines where to go after authenticating.
I dug through the Microsoft.Owin.Security.WsFederation
source code using ILSpy, and I figured out that there was a property I was not setting: AuthenticationProperties.RedirectUri
. This RedirectUri
property needs to be set before redirecting to the STS, as it needs to be used later in InvokeAsync
after authenticating. I have an AuthenticateAllRequests
method and I initialize this AuthenticationProperties
object there:
private static void AuthenticateAllRequests(IAppBuilder app, params string[] authenticationTypes)
{
app.Use((context, continuation) =>
{
if (context.Authentication.User?.Identity?.IsAuthenticated ?? false)
{
return continuation();
}
else
{
AuthenticationProperties authProps = new AuthenticationProperties
{
RedirectUri = context.Request.Uri.ToString()
};
context.Authentication.Challenge(authProps, WsFederationAuthenticationDefaults.AuthenticationType);
return Task.CompletedTask;
}
});
}
To summarize more concretely: let's say my OWIN startup URL is http://myapp.com/owin. So then I set Wtrealm = "http://myapp.com/owin"
. Let's say a user goes to http://myapp.com/owin/coolstuff, and let's say they are signed into the authentication server with SSO, but they don't have the cookie for my app. In this case WS-Federated authentication needs to be used. http://myapp.com/owin/coolstuff is set as AuthenticationProperties.RedirectUri
before redirecting to the STS so that it is put in the wctx
query parameter, and can therefore be used after returning back to the OWIN app.
I was getting confused before because after returning from the STS, I would see IOwinRequest.Uri = "http://myapp.com/owin"
, and I would assume that was the final URL, and that the user's original request was lost. Now it makes a lot more sense that that is the URL the STS redirects to in order to finish authentication, but the final redirect URL, http://myapp.com/owin/coolstuff, is stored for redirecting after authentication finishes.
I am pretty sure this is how it works, but I may be wrong and if anyone has anything to add I'd love to hear it.