Freigeben über


Windows Authentication, ADFS and the Access Control Service

Its just a few weeks ago that I started looking in more detail at the Windows Azure AppFabric Access Control Service (or just ACS to its friends) and one of the first things I wanted to figure out was how to federate between my on-premise Active Directory domain and ACS. With a few pointers, its not too tricky, but I figured I’d walk you through the whole process anyhow!

I’m going to assume that you’ve got an account set up with AppFabric Labs (https://portal.appfabric.labs.com) and an Active Directory domain installed too – if you don’t (or you want play about on a test domain) then just spin up a VM running Windows Server 2008 (or R2) and Install Active Directory Domain Services.

Here’s what we’ll do:

  1. Download and Install Active Directory Federation Services (ADFS) v2.0
  2. Configure a certificate to use with ADFS
  3. Setup ADFS and enable Endpoints for Windows Authentication
  4. Establish a Trust relationship from ADFS to ACS
  5. Provision a Service Namespace  X.509 Certificate in ACS
  6. Establish a Trust relationship from ACS to ADFS
  7. Create Rules to pass through claims from ADFS to the Relying Party Application via ACS
  8. Use the Windows Identity Foundation (WIF) to get a SAML token from ADFS
  9. Submit the SAML token to ACS to retrieve a set of claims as a SWT token

Download and Install ADFS v2

  1. Download the appropriate version of the ADFS v2 update from the Microsoft Download Center.
  2. Install ADFS, you’ll probably want to Install a Federation Server.
  3. When setup completes, launch the AD FS 2.0 management console.

Create a Self-Signed SSL Certificate

Before you can configure ADFS you’ll need an SSL Certificate configured on your machine – if you have one already, skip ahead to the next step. Otherwise take you can setup a self-signed (untrusted) certificate using IIS. It goes without saying that you shouldn’t use a self-signed certificate in a production environment!

  1. Open the IIS Management Console

  2. In the Connections pane on the left hand side, select your server

  3. Double click on Server Certificates then select ‘Create Self-Signed Certificate’ from the actions pane on the right:

    image

  4. Type a name for the certificate – I’ve named mine ‘Self-Signed Certificate’ (creative, huh?)

Configure ADFS and Enable Windows Authentication Endpoints

  1. From the AD FS 2.0 Management console, start the Server Configuration Wizard
    1. Create a New Federation Service on a Stand-alone federation server.
    2. In the third step, choose your Self-Signed Certificate – of course, if you already have a trusted, chained certificate for your server, you should use that instead!
    3. Click Finish and ADFS will compete the configuration of your new Federation Server
  2. Next, we’ll enable some Endpoints to allow us to retrieve SAML tokens from ADFS using our Windows Credentials. We’ll do this over the WS-Trust 1.3 Protocol using Transport Security with a Message Credential.
    1. Expand the Service element of the navigation pane (left) and select Endpoints

    2. Scroll down the list and select the /adfs/services/trust/13/windowsmixed endpoint – right click and Enable it.

      image

    3. You’ll be alerted that you need to restart the AD FS 2.0 Service – open up the Services manager (services.msc) and restart the AD FS 2.0 Windows Service. You can read more about the various available ADFS endpoints on TechNet.

Establish a Trust relationship from ADFS to ACS

So, ADFS is now acting as an STS (Security Token Service) for your Active Directory domain, this means that you’re ready to setup federated authentication with other STSs or Identity Providers like the Windows Azure AppFabric Access Control Service (ACS). To do that, we need to establish a bidirectional trust relationship between ADFS and ACS – this allows your ACS project to process incoming tokens from your Active Directory and ensure that Active Directory trusts ACS and will issue tokens for use with it. We’ll be using AppFabric Labs for this – its free, so go Sign Up for an account if you don’t have one already.

  1. Visit the Windows Azure AppFabric Portal. Click on your project to bring up a list of your service namespaces, then choose ‘Access Control’ from the service namespace you want to issue tokens for from ADFS:

    image

  2. On the next page, click the big, orange ‘Manage’ button to view the Access Control Service management UI. On the left, you’ll see a set of links directing you to common ACS Administrative tasks:

  3. To add a new Identity Provider to your ACS Service Namespace you (unsuprisingly) use the Identity Providers link

  4. Choose ‘Add Identity Provider

  5. Since we just spent a while setting up ADFS, you’ll want to Add a ‘Microsoft Active Directory Federation Services 2.0’ Identity Provider

  6. Choose a sensible display name (or a wacky one if like, its really up to you)

  7. Upload some Federation Metadata.

    • If (like me) you’re using an ADFS installation which doesn’t have an Internet Facing IP Address (i.e., there’s no way to access your ADFS server from the public internet) then you’ll want to save a copy of your federation metadata as a UTF-8 Xml file and use the upload button. You can find the metadata at https://[Your-ADFS-Server]/FederationMetadata/2007-06/FederationMetadata.xml .
  8. Specify a login-link name (the Display Name is probably a good choice!)

  9. Click ‘Save

  10. You’re returned to the list of Identity Providers, but now it contains your ADFS STS – simple, huh?

Next, we need to perform a very similar process on your ADFS server, establishing a trust relationship back to ACS.

Establish a Trust relationship from ACS to ADFS

ADFS won’t issue tokens for use against the Access Control Service if it doesn’t trust it – we’ll need to configure a default signing certificate in ACS, generate some Federation Metadata and share that with our ADFS instance to finish off the establishment of this trust relationship.

  1. Use the steps above to get to the Access Control Management page for your service namespace.

  2. In the ‘Develop’ area, click on ‘Application Integration

  3. Towards the bottom of this page, you’ll see an Endpoint Reference for WS-Federation Metadata – copy and paste the link into your address bar and hit enter.

  4. At this point, you’ll either see some Federation Metadata or – more likely – a wonderful error message, if you don’t see the error, skip ahead to Step 15:

    image

  5. This happens because you don’t have a default certificate configured for signing – we’ll go upload one and then gain access to our precious metadata.

  6. Go back to the Access Control Management page and click Certificates and Keys

  7. You need to Add a Token Signing Certificate, so click on the relevant link

  8. Ensure that X.509 is selected as the certificate type and enter a name.

  9. You’ll need to generate a certificate which you can do using the MakeCert tool which is part of the Windows SDK (if you have VS2010, you already have this installed, just start a Visual Studio Command Prompt and type ‘MakeCert’.

    • MakeCert.exe -r -pe -n "CN= <Your-Service-Namespace> .accesscontrol.appfabriclabs.com" -sky exchange -ss my
  10. In the same command prompt window, use CertUtil to find and then export the certificate you just created:

    1. Run CertUtil to retrieve details of the certificate that was just created. Note down the SHA1 Cert Hash (it’s a hex string like: b4 24 b5 c2 ef 40 5c 26 76 38 12 02 95 c1 56 5f 18 25 4f):
      • CertUtil -user -store My <Your-Service-Namespace> .accesscontrol.appfabriclabs.com
    2. Run CertUtil again to export the private key. You need to specify a password, the SHA1 hash and a filename for the PFX that will be created:
      • CertUtil -user –p <Some-Password> –exportpfx “ <Your-SHA1-Cert-Hash><Output-Filename.pfx>
  11. Back at the portal, upload the private key (.pfx) file you just creates and enter the password you specified to CertUtil

  12. In the Used For field, specify Service Namespace

  13. Click Save

  14. Browse back to the metadata URL from step 4: this time, no error and lots of lovely metadata!

  15. If your ADFS server doesn’t have internet access, save this federation metadata to file and transfer it to the ADFS Server

  16. Login to your ADFS Server and Start the AD FS 2.0 Management Console.

  17. In the Console Tree (Left), right click on ‘Relying Party Trusts’ under the ‘Trust Relationships’ node and choose ‘Add Relying Party Trust’.

  18. A wizard pops up, skip past the welcome page to ‘Select Data Source’ and either enter the URI of your federation metadata (something like: .accesscontrol.appfabriclabs.com/FederationMetadata/2007-06/FederationMetadata.xml">https://<Your-Service-Namespace>.accesscontrol.appfabriclabs.com/FederationMetadata/2007-06/FederationMetadata.xml)  or upload the xml document if you saved it in step 15.

  19. Specify a display name and choose your issuance rules policy – for simplicity’s sake I’m going to select ‘Permit All Users to Access this Relying Party

  20. On the next page, confirm all the data you’ve entered and hit finish.

When you’re done, leave the ‘Open the Edit Claim Rules dialog’ option checked – we’ll use this dialog to add some rules to this trust relationship allowing data to flow from Active Directory and into ACS. To get back to this dialog, right click on the Relying Party Trust for ACS and click ‘Edit Claim Rules’

  1. On the Issuance Transform Rules tab, click Add Rule.

  2. Select ‘Pass Through or Filter an Incoming Claim’ as the template to use and click Next

  3. Now we’ll configure a claim (i.e. piece of identity data like role membership, username, etc.) to be passed through to ACS – just select the claim from the drop down list, specify a name and click Finish:

    image

  4. Repeat steps 1-thru-3 to add additional claims to pass thru to ACS (if you feel like it!)

At this point, some congratulations are in order: you’ve installed and configured ADFS, you’ve created and uploaded a certificate to allow signing of your ACS Federation Metadata, you’ve established a bi-directional trust relationship between ADFS and ACS and you’ve specified some claims which can be passed through to the Access Control Service. There’s just a little more work ahead of you before you can perform that first piece of federated, claims-based authentication.

  1. Head back over to the Windows Azure AppFabric portal and get yourself back to the Access Control Service Management page we’ve been using in the past couple of sections. This time, click the ‘Rule Groups’ link.
  2. Select the rule group you want to modify.
    • Rule Groups define how claims from one party are transformed by the Access Control Service before being passed on to another (for example, only passing through the ‘First name’ claim from Facebook or ADFS to a service located at a specific Url). You associate Rule Groups with Realms (that is, the URI of a resource which is secured by the Access Control Service) on the Replying Party Application page. By default, there’s just one Rule Group and just one Relying Party Application. Feel free to add more!
  3. Add a rule to the rule group with the ‘Add Rule’ link- we’re going to configure the windows account name to pass through, but you can repeat this process to forward or transform as many claims as you want/need (full name, email address, etc.).
  4. Specify the incoming claim details in the ‘If… ’ section. In the Claims Issuer area, choose your new ADFS Identity Provider, next choose the Input claim type that you want to pass through. I’m going to pass through the Windows Account Name which has the type https://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname . I’m going to accept any input claim value.
  5. Since we’re just going to pass through this claim (the Windows Account Name), select ‘Pass through’ in both the Output Claim Type and Output Claim Value sections. You could (of course) perform more complex rule processing here if you chose to.
  6. Click the Save Button

We’re almost done. Now that we’ve established a claims processing rule (pass through the Windows Account Name) associated with our newly trusted identity provider (your Active Directory Federation Server) we can fire up Visual Studio and write some code. In this next section, we’ll get a SAML Token from ADFS and exchange it for a SWT token via the access control service. Once you’ve got the Simple Web Token (SWT) the Authentication World becomes Your Oyster – go and use it to auth against web services/sites or anything else!

Use the Windows Identity Foundation to get a SAML token from ADFS

There are a couple of things we need to do to get a token from ADFS. Firstly, we’ll need to validate that Self-Signed Certificate we created a while ago for securing SSL traffic to and from the ADFS server, secondly we’ll use the Windows Identity Foundation to request the token from an ADFS endpoint.

  1. Download and Install the Windows Identity Foundation (WIF) and Windows Identity Foundation SDK.

  2. Create a new .Net Framework 4.0 Console Application in Visual Studio 2010

  3. Add References to the Microsoft.IdentityModel, System.IdentityModel and System.ServiceModel assemblies.

  4. Add a method, , to validate the SSL Certificate from ADFS (since the certificate we’re using isn’t chained to a root certification authority we’ll need to explicitly say that its OK to trust). Use the code below, but be sure to replace my ADFS server name (vm-willpe-adfs.willpe.sb) with the fully qualified DNS domain name of your ADFS Server:

     private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        return sslPolicyErrors == SslPolicyErrors.None
            || string.Equals(certificate.Issuer, "CN=vm-willpe-adfs.willpe.sb", StringComparison.InvariantCultureIgnoreCase);
    }
    
  5. Next, we'll override the default Certificate Validation Callback - add this line to the Main method of Program.cs

     ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidationCallback;
    
  6. Now we need to make some calls into the Windows Identify Foundation to request a SAML token from ADFS. There are a couple of things to note here. First up, when we enabled endpoints in ADFS, we selected a WS-Trust 1.3 Endpoint with Windows Authentication and Mixed Mode security. This means that we need to use version 1.3 of WS-Trust, specify Windows Credentials and use Transport Security with a Message Credential when connecting. We’ll also have to use the full URL to the endpoint we enabled in ADFS. Here’s the code to create a WS-Trust Channel factory compatible with this endpoint. As usual, don’t forget to change my ADFS hostname (vm-willpe-adfs) to yours:

    
    var stsUrl = "https://vm-willpe-adfs/adfs/services/trust/13/windowsmixed";
    
    WSTrustChannelFactory trustChannelFactory =
        new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
            new EndpointAddress(new Uri(stsUrl)));
    
    trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
    
    trustChannelFactory.Credentials.Windows.ClientCredential.Domain = "<<Your NetBios Domain Name Here >>;
    trustChannelFactory.Credentials.Windows.ClientCredential.UserName = @"<<Your Domain Username Here>>";
    trustChannelFactory.Credentials.Windows.ClientCredential.Password = @"<<Your Domain Password Here>>";
    
  7. With a trust channel factory instantiated and configured, you can use a RequestSecurityToken to make the token request. Note that you’ll need to specify the Url that the token applies to – this is going to be your ACS Management Url:

     var acsUrl = "https://willpe-blog.accesscontrol.appfabriclabs.com/";
    
    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;
    
  8. Putting it all together, you’ll want to add the method ‘GetSamlToken’ to Program.cs: 

     private static string GetSamlToken(string domain, string userName, string password)
    {
        var acsUrl = "https://willpe-blog.accesscontrol.appfabriclabs.com/";
        var stsUrl = "https://vm-willpe-adfs/adfs/services/trust/13/windowsmixed";
    
        WSTrustChannelFactory trustChannelFactory =
            new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                new EndpointAddress(new Uri(stsUrl)));
    
        trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
        trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
        trustChannelFactory.Credentials.Windows.ClientCredential.UserName = userName;
        trustChannelFactory.Credentials.Windows.ClientCredential.Password = password;
    
        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();
        }
    }
    

Add a line to the Main method to call GetSamlToken and verify that you get a nice, chunky XML token back from ADFS. With just a couple more lines of code, all of that markup will become a Simple Web Token.

Submit the SAML token to ACS to retrieve a set of claims as a SWT token

The hard work is over now – all we need to do is submit the SAML token we retrieved to ACS and get a Simple Web Token in response. With that token, you’re good to authenticate against any (properly configured) resource secured by the AppFabric Access Control Service.

  1. Add the method GetSimpleWebToken(string samlToken) to Program.cs

  2. Retrieve your ACS STS Uri from the portal, it’s probably something like: https://[Your-Service-Namespace].accesscontrol.appfabriclabs.com/WRAPv0.9/

  3. Upload the SAML Token to ACS. You need to specify the Scope (that is, the ‘Applies To’/’Expected Audience’ Url or the address of the resource which this token is to be used against), the assertion format (SAML in this case) and the assertion itself. I’m using this token to authenticate directly against the ACS management service, so my parameters look like this:

     NameValueCollection parameters = new NameValueCollection();
    parameters.Add("wrap_scope", "https://willpe-blog.accesscontrol.appfabriclabs.com/mgmt");
    parameters.Add("wrap_assertion_format", "SAML");
    parameters.Add("wrap_assertion", samlToken);
    
  4. Then we’ll simply upload these values to the Access Control Service and extract the SWT token from the response. Putting it all together, the GetSimpleWebToken method looks like this:

     private static string GetSwtToken(string samlToken)
    {
        var appliesTo = "https://willpe-blog.accesscontrol.appfabriclabs.com/mgmt";
        var acsStsUrl = "https://willpe.accesscontrol.appfabriclabs.com/WRAPv0.9/";
    
        try
        {
            WebClient client = new WebClient();
            client.BaseAddress = acsStsUrl;
    
            NameValueCollection parameters = new NameValueCollection();
            parameters.Add("wrap_scope", appliesTo);
            parameters.Add("wrap_assertion_format", "SAML");
            parameters.Add("wrap_assertion", samlToken);
    
            byte[] responseBytes = client.UploadValues("", parameters);
            string response = Encoding.UTF8.GetString(responseBytes);
    
            return Uri.UnescapeDataString(response
                .Split('&')
                .Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
                .Split('=')[1]);
        }
        catch (WebException wex)
        {
            string value = new StreamReader(wex.Response.GetResponseStream()).ReadToEnd();
            throw;
        }
    }
    

So, there we go – we’ve installed and configured ADFS, Created a Trust Relationship between the AppFabric Access Control Service and your new ADFS Instance, we configured ACS to accept incoming claims from the Active Directory Identity Provider and pass-through the Windows Account name. Then we used WIF to get swap our Windows Credentials for a SAML token which we sent to ACS to get a Simple Web Token. The result? A little string like this:

https://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname=**WILLPE\\Test**

&https://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider=https://VM-WILLPE-ADFS.willpe.sb/adfs/services/trust

&Audience=https://willpe-blog.accesscontrol.appfabriclabs.com/mgmt

&ExpiresOn=1288060895

&Issuer=https://willpe-blog.accesscontrol.appfabriclabs.com/

&HMACSHA256=[Base64-Hash-Code]

You can download the full sample from SkyDrive – feel free to leave any comments/questions below or find me on Twitter (@willpe)

I hope this helps,

Will

Comments

  • Anonymous
    October 27, 2010
    Thanks for sharing this valuable information friend.Really you have mentioned it with a great detail.

  • Anonymous
    April 30, 2012
    Hi WIll , You have mentioned the AppFabric Labs link as portal.appfabric.labs.com . You may want to correct/update it to portal.appfabriclabs.com .

  • Harish
  • Anonymous
    November 26, 2013
    Hi! Thanks for your correct informations, you are providing some good helpful informations. Are you success running the newest version of ME? Thanks a lot

  • Anonymous
    September 17, 2015
    Hello, I don't understand. If you use windowsmixed "adfs/services/trust/13/windowsmixed", why do you have to set the username and password? shouldn't it authenticate automatically via ntlm? Thank you