Sometimes it is useful to have a really simple way to acquire a token from a token service – without having to fiddle around with WS-Federation or WS-Trust. Issuing a simple GET request against a token issuance endpoint seems to fulfill that requirement.
So I decided to a add a simple HTTP endpoint to my STS using the WCF web programming model:
[ServiceContract]
public interface IRestfulTokenServiceContract
{
[OperationContract]
[WebGet(UriTemplate = “/?realm={realm}”)]
XElement Issue(string realm);
}
You could provide more parameters here (like token type, lifetime etc.) but i decided to keep it simple.
For the implementation you have to decide which authentication types you want to support. Since I needed username/password authentication I used Cibrax’ excellent basic authentication extension. If you need to support client certificates, you would get the certificate details from WCF’s AuthorizationContext.
To route the GET request to the existing token issuance logic, you can create the STS using the static CreateSecurityTokenService method on the SecurityTokenServiceConfiguration class. Then you have to construct a RST and IClaimsPrincipal to describe the token request and pass that into the Issue method. Afterwards you serialize the security token back as a HTTP response and you are done.
I will incorporate that into the next drop of the StarterSTS – but for now here is the code:
public class RestfulTokenService : IRestfulTokenServiceContract
{
public XElement Issue(string realm)
{
EndpointAddress epRealm;
try
{
epRealm = new EndpointAddress(realm);
}
catch
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.BadRequest;
return null;
}
RequestSecurityToken rst = new RequestSecurityToken
{
AppliesTo = epRealm,
KeyType = KeyTypeConstants.Bearer
};
var sts =
new StarterTokenServiceConfiguration().CreateSecurityTokenService();
var rstr = sts.Issue(CreatePrincipal(), rst);
StringBuilder sb = new StringBuilder();
var writer = XmlWriter.Create(sb);
var col =
SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
col.WriteSecurityToken(writer,
rstr.RequestedSecurityToken.SecurityToken);
writer.Flush();
WebOperationContext.Current.OutgoingResponse.ContentType =
“text/xml”;
return XElement.Parse(sb.ToString());
}
private IClaimsPrincipal CreatePrincipal()
{
if (Thread.CurrentPrincipal == null ||
Thread.CurrentPrincipal.Identity == null ||
string.IsNullOrEmpty(Thread.CurrentPrincipal.Identity.Name))
{
throw new InvalidRequestException(“unknown client”);
}
var identity = new ClaimsIdentity(
new Claim(WSIdentityConstants.ClaimTypes.Name,
Thread.CurrentPrincipal.Identity.Name));
return new ClaimsPrincipal(identity);
}