Adding a REST Endpoint to a WIF Token Service

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);
    }

This entry was posted in IdentityModel. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s