ACS SAML / ADFS v2 Sample


The November 2009 CTP of ACS integrates with Active Directory Federation Server v2. ACS can act as a bridge between enterprise identity and REST web services.

The runtime flow is pretty simple (shown below).

image

  1. At runtime, the client app requests a SAML bearer token from AD FS v2. The easiest way to do this is with Windows Identity Foundation (WIF).
  2. The client app POSTs the SAML token to ACS over SSL. ACS uses configurable rules to calculate the claims in a Simple Web Token (SWT), creates a SWT, signs it, and returns it to the client app. The protocol for this exchange is OAuth WRAP.
  3. Next, the client packages the SWT in the HTTP Authorization header and sends it to the REST web service along with whatever payload the REST web service requires.
  4. Once the REST web service receives the token & payload, it validates the token and checks the claims in the token. The REST web services allows or denies access based on the outcome.

Viola. You have a REST web service that integrates with AD FS v2 via OAuth WRAP and SWT.

Mini AD FS setup (for this scenario only)

There is some setup required to enable this scenario (other than acquiring an ACS Service Namespace). For starters, you’ll need an AD FS v2 server. Since this requires a domain, I’ve provided a service that replicates the basic token issuing behavior of AD FS (at the bottom of this post).  The only relying party trusted by this service is ACS.

To setup the service, you’ll need to update the App.config file. Update the “signingCertName” to a cert in your LocalMachine / Personal cert store. Also update the “serviceNamespace” to your ACS service namespace.

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
  <appSettings> 
    <add key="signingCertName" value="CN=localhost"/> 
    <add key="stsBaseAddress" value="localhost/miniadfs"/> 
    <add key="stsPath" value="Trust/13/Windows"/> 
    <add key="serviceNamespace" value="justinpdcdemo"/> 
    <add key="acsHostname" value="accesscontrol.windows.net"/> 
  </appSettings> 
</configuration>

You’ll also have to setup SSL for your IIS install (http://learn.iis.net/page.aspx/144/how-to-setup-ssl-on-iis-70/)

You’ll also need to install the WIF RC. Available here: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=defd2019-a61f-4327-9332-6a4b6103527a#tm

From there, you should be able to run the service.

Fed Metadata Setup with ACS

After you have the mini ADFS service running, you’ll want to use the Fed Metadata it publishes to create an issuer in ACS. Also in the sample below is some code that shows you how to programmatically do that.

If you’d rather use a tool, you can use the Management Browser (http://code.msdn.microsoft.com/acmbrowser).

Simply create a new Issuer, select FedMetadata from the Algorithm drop down, and set the URL of the fed metadata server. In the miniADFS server, that URL is https://localhost/LocalADFSv2/FederationMetadata/2007-06/FederationMetadata.xml

image

Creating a Scope & Rule for the new Issuer

Next, you’ll want to create a scope and a rule that refers to that issuer. The sample at the bottom of this post uses a scope with an applies_to URI of http://localhost/samltest. You can use the Management Browser to create one.

With the scope in place, we can create a rule. All rules require the name of the Issuer and a claim type in the antecedent. When you create an Issuer using Fed Metadata, the Issuer name is fixed in the Fed Metadata. My MiniADFS server uses an issuer name of https://localhost/miniadfs/Trust/13/Windows. It also spits out claims of type http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name.

With that data, you can create a Passthrough rule. Passthrough rules basically countersign the input claims. In this case, a passthrough rule would countersign any http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name claim issued by the issuer https://localhost/miniadfs/Trust/13/Windows. The consequent of the rule can be of any type you choose. To keep the token compact, I’ll use a claim type of “name”.

You can set all this up using the management browser, as shown below.

image

Acquiring A SAML Token

With the Issuer, Scope, and Rule setup, let’s get a SAML token using WIF (the RC). The code for doing this is in the SAMLClient project from the code sample in this post. The WIF code is pretty straightforward:

private static string GetSAMLToken()
{
    WSTrustChannelFactory trustChannelFactory =
        new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
            new EndpointAddress(new Uri(samlUrl)));

    trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;

    try
    {
        RequestSecurityToken rst =
            new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer);
        rst.AppliesTo = new EndpointAddress(acsUrl);
        rst.TokenType = Microsoft.IdentityModel.Tokens.SecurityTokenTypes.Saml2TokenProfile11;

        WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
        GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken;
        string tokenString = token.TokenXml.OuterXml;
        return tokenString;
    }
    finally
    {
        trustChannelFactory.Close();
    }
}

The only trick is to ensure you are using the Bearer key type (Yes, you can use WIF to request a Bearer token).

Using the SAML token to get a SWT

Next, you can use the SAML token to request a SWT from ACS:

private static string SendSAMLTokenToACS(string samlToken)
{
    try
    {
        WebClient client = new WebClient();
        client.BaseAddress = acsUrl;

        NameValueCollection parameters = new NameValueCollection();
        // ensure the applies_to URI is created in your ACS
        // service namespace
        parameters.Add("applies_to", "http://localhost/samltest");
        parameters.Add("wrap_SAML", samlToken);

        byte[] responseBytes = client.UploadValues("", parameters);
        string response = Encoding.UTF8.GetString(responseBytes);

        return response
            .Split('&')
            .Single(value => value.StartsWith("wrap_token=", StringComparison.OrdinalIgnoreCase))
            .Split('=')[1];
    }
    catch (WebException wex)
    {
        string value = new StreamReader(wex.Response.GetResponseStream()).ReadToEnd();
        throw;
    }
}

Viola! That’s all there is.

Here’s the full code sample – Let me know any feedback you have…

Comments (11)

  1. Will WIF be updated in the future to include support for SWT and OAuth WRAP or will a framework be created for working w/ these protocols in a more natural and seamless way?

  2. justinjsmith says:

    Travis,

    We are investigating that now…

  3. Hi Justin,

    my question is … how the ACS can trust the saml bearer token? It is able to check the signature? How?

    If, at the moment, this is not supported, you plan to support it in the future?

    Thanks in advance

  4. justinjsmith says:

    Yes, ACS checks the signature of the SAML token. The signing cert can be embedded in WS-Fed metadata (ACS can read it), or you can upload the X509 yourself.

  5. Arun says:

    The sample uses Wrap 0.8 ACS has Wrap0.9 endpoints do you have the updated sample?

  6. Legar says:

    Hi Justin,

    I have a problem trying to create the Issuer with FedMetadata Algorithm. Studying the code of the ACMBrowser apparently is necessary set an IssuerName and a Security to create the issuer, but I can not find what is the security type required to configure correctly the issuer. I try with this modification in the last code of the ACMBrowser, in the ConversionHelper class:

    public static Issuer ToIssuer(this IssuerXml issuerXml)

           {

               Issuer issuer = new Issuer();

               issuer.InitializeResource(issuerXml);

               if (string.IsNullOrEmpty(issuerXml.FedMetadataUri))

               {

                   issuer.IssuerName = issuerXml.IssuerName;

                   Security security = new Security();

                   security.Algorithm = issuerXml.Algorithm;

                   security.CurrentKey = issuerXml.CurrentKey;

                   security.PreviousKey = issuerXml.PreviousKey;

                   issuer.Security = security;

               }

               else

               {

                   // the IssuerName and Security fields cannot be specified along with FedMetadata ****This is incorrect apparently

                   //MY CHANGES

                   //issuer.IssuerName = null;

                   //issuer.Security = null;

                   issuer.IssuerName = issuerXml.Handle;  //To obtain a name for the issuer

                   Security security = new Security();

                   security.Algorithm = issuerXml.Algorithm;   //This set "FedMetadata" in the security.Algorithm

                   security.CurrentKey = issuerXml.CurrentKey; //This set nothing because issuerXml.CurrentKey is empty

                   security.PreviousKey = issuerXml.PreviousKey;  //This set nothing because issuerXml.PreviousKey is empty

                   issuer.Security = security;

                   //MY CHANGES

               }

               issuer.FederationMetadata200706 = FederationMetadata(issuerXml.FedMetadataUri);

               return issuer;

           }

    But this is the new message error "The Algorithm request parameter value ‘FedMetadata’ is invalid for the following reason: the provided algorithm type ‘FedMetadata’ is not supported"

    Then I need to know what is the correct security type for this.

    Can you help me, please?

  7. Badal says:

    Hi Justin,

    Interesting post. In my application scenario…

    assume i have a web role which trusts token only from ACS. I want to configure ACS to trust the tokens from various issuers running on different partner locations. So the flow is like

    1. When someone tries to access a web application deployed on azure (web role). Web role redirects user to ACS with Whr querystring attribute to request a token.

    2. ACS redirects to appropriate identity provider depending on Whr parameter (How to do this?)

    3. IP authenticates user and creates a SAML token which is then posted back to ACS

    4. ACS extracts the claims and transforms it as per the aplication requirement

    Do we have any sample / tutorial on how to perform this?

    Regards

    Badal

  8. Juan Suero says:

    we have a major application, built in Sharepoint 2010 RC, that authenticates users with Active Directory using a web form.  the code also leverages a couple of AD attributes to redirect the user to the right sharepoint site depending on what organization they are coming from… in AD they set up an OU with different sub OUs for each org and somehow they authenticate and figure out which site the user should go to based on this custom web form installed into sharepoint 2010.

    I know 2010 supports Claims auth.

    I know you guys have adfs20 that bolts onto AD

    How do i think about re-architecting this solution so i can offload user account management to each organization?

    if i install ADFS20 on these AD servers

    do i federate with BPOS somehow?

    then each company can get a slice of BPOS they can manage thier accounts

    we can then translate BPOS roles or groups to whatever application roles or groups they are using?

    thanks.

  9. VDeevi says:

    With ACS we can have only one Identity providers for Windows Live ID, Yahoo, Google etc. We want to know about how many ADFS 2.0 Identity providers can be added to ACS? Is this allows us to configure multiple ADFS 2.0 Identity Providers as we want to build a claims aware application which uses both ADFS and custom STS for providing the claims.

    Please provide us some information in this regard…

    Thanks,

    VDeevi.

  10. VDeevi says:

    With ACS we can have only one Identity providers for Windows Live ID, Yahoo, Google etc. We want to know about how many ADFS 2.0 Identity providers can be added to ACS? Is this allows us to configure multiple ADFS 2.0 Identity Providers as we want to build a claims aware application which uses both ADFS and custom STS for providing the claims.

    Please provide us some information in this regard…

    Thanks,

    VDeevi.

  11. freak says:

    ACS v2 does support multiple ADFS 2.0 IdPs.

    not sure about what's the upper limit it supports … Justin coluld you please tell?

Skip to main content