In general I think the API design of the WS-Federation support in WIF / .NET 4.5 is a bit unfortunate.
It was a strange decision to combine the HTTP module (aka the FAM) and the more generic protocol helpers into a single class. And the fact the system.identityModel configuration sections are not declared by default, makes the FAM hard to use as a “standalone” library (for the search engines: “ID7027: Could not load the identity configuration because no <system.identityModel> configuration section was found.”). Microsoft?! Please fix this.
That all in combination makes it non-obvious how to “manually” process WS-Federation messages and since the question came up recently – here’s how to do it with ASP.NET Web API:
To create the WS-Federation request you can use this code:
public HttpResponseMessage Get()
{
var signInRequest = new SignInRequestMessage(
new Uri(“https://idsrv.local/issue/wsfed “),
“urn:realm”);
var response = Request.CreateResponse(
HttpStatusCode.Found);
response.Headers.Location =
new Uri(signInRequest.WriteQueryString());
return response;
}
The interesting bit is processing the response. As long as you can turn the post data into a NameValueCollection, it’s quite easy:
public HttpResponseMessage Post(HttpRequestMessage request)
{
var form = request.Content.ReadAsFormDataAsync().Result;
var signInResponse = WSFederationMessage.CreateFromNameValueCollection(
FederationMessage.GetBaseUrl(request.RequestUri),
form) as SignInResponseMessage;
var fam = new WSFederationAuthenticationModule();
// set all the necessary configuration
// don't forget to declare the system.identityModel config sections
fam.FederationConfiguration = new FederationConfiguration();
var token = fam.GetSecurityToken(signInResponse);
// validate token etc.
}
HTH
Can this technique be used to “manually” get a SessionSecurityToken from the ThinkTecture IdentityServer? How? I don’t exactly understand where should this code be placed, and how is it that no credentials are passed to authenticate with the Issuer/IDP ?
Not sure what you mean – IdentityServer does not issue session tokens to RPs.
This was mainly for educational purposes to show it is possible to handle the WS-Fed response in an environment “outside” of ASP.NET, e.g. (self hosted) Web API.
I’ll explain: I have an MVC web application configured to passively authenticate using IdentityServer, then using an HttpClient to call a WEB API application sending the SAML token from the bootstrap context in the security header, of course the WEB Api is configured to handle the SAML token. This works great!
What I was now trying to do, was to allow an invited guest to view parts of the site. By custom validating it’s invitation token, then “progmatically” authenticate with Identity server, get a security token, put it in a session cookie using the SAM, and in the bootstrap context to allow the same code that handles a registered user to work untouched (getting the SAML from the bootstrap context and using it to authenticate with the WEB API. Transforming the claims on the way would also be useful, and maybe even using some kind of delegation/ActAs, to allow the Guest to act as the user who invited him.
So, am I’m trying to do something very awkward? What is the way I’m supposed to use WIF/Thinktecture infrastructure to achieve my goal? I still couldn’t find all the puzzle pieces to make this fit together…
Well – OK. Why not.
You can programmatically request tokens using WS-Trust. Manually creating the session token with the bootstrap token should work as well – though I never tried it.
I tried, but couldn’t find a way to convert the GenericXMLSecurityToken received from the wstrust endpoint to a SessionSecurityToken, required by the sam.WriteSessionTokenToCookie(token), is there a way for doing that?
Well – OK. Why not.
You can programmatically request tokens using WS-Trust. Manually creating the session token with the bootstrap token should work as well – though I never tried it.
You first need to turn it into a SAML token – i think tt.idm has a ToSecurityToken() extension method. Then validate it to get the ClaimsPrincipal.
Then turn into a SessionSecurityToken.