During my research into Access Control Services (ACS) I ran into a couple of instances of people wondering about how to deploy an ACS authentication solution to Azure staging. The tricky part is that the ‘audience URI’ list is in the web.config file, which can’t modified after deployment – and this list should include the URL of the site, which is generated during deployment for Azure staging. Catch 22.
I figured there should be a way to modify the configuration setting programmatically, and after some fiddling I’ve found one.
The first step is to hang onto the configuration object at the time that it’s created, by putting it in the application state
void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
// hang onto this object for later; we'll need to add ourselves to the audience uri list
Application["ServiceConfiguration"] = e.ServiceConfiguration;
it’s probably possible, but not convenient, to construct the correct URL at this point, in a generic way. In Azure, there are meaningless port numbers attached to the actual HttpRequest.Url, so we’re going to build the URL using this special method:
protected string GetApplicationUri()
{
//
// In the Windows Azure environment, build a wreply parameter for the SignIn request
// that reflects the real address of the application.
//
HttpRequest request = HttpContext.Current.Request;
Uri requestUrl = request.Url;
var wreply = new StringBuilder();
wreply.Append(requestUrl.Scheme); // e.g. "http" or "https"
wreply.Append("://");
wreply.Append(request.Headers["Host"] ?? requestUrl.Authority);
wreply.Append(request.ApplicationPath);
if (!request.ApplicationPath.EndsWith("/"))
wreply.Append("/");
return wreply.ToString();
}
The place to inject the dynamically generated Uri is during the redirect to ACS; at this point we’ve got the right information in the host header to reliably construct our URL.
void WSFederationAuthenticationModule_RedirectingToIdentityProvider(
object sender,
RedirectingToIdentityProviderEventArgs e)
{
var wreply = GetApplicationUri();
e.SignInRequestMessage.Reply = wreply;
e.SignInRequestMessage.Realm = wreply;
// Our our uri to the list of those we'll accept in the incoming token
var myUri = new Uri(wreply);
var allowedUris = ((ServiceConfiguration)Application["ServiceConfiguration"])
.AudienceRestriction.AllowedAudienceUris;
if (!allowedUris.Contains(myUri))
allowedUris.Add(myUri);
}
The last step is to go into the ACS settings (I’m using the AppFabric Labs for ACS,) and add the staging environment to the relying parties list, after it’s been deployed and you’ve got the generated URL for staging.
Is it secure?
Hmm, good question – I think the answer is yes. The host header comes from the client, and I’m not sure whether or how well IIS on Azure validates that this header corresponds to the site actually being accessed. So the potential security weakness would be to request the login page with a spoofed host header, then post back to the site with a forged token, thereby bypassing WIF’s check that the incoming token match a configured allowed Uri. However, I don’t think it would be possible to obtain a forged token that would pass the other security measures in place in the website and the ACS configuration itself.
In any case, it would be simple enough to modify this code to only use host headers for a staging site, which would limit spoofing and get you some security through obscurity (which probably has a lot to do with the GUID dynamic URLs in the first place.)