Gavin Draper's Blog

Father, Developer, Tech Geek, Extreme Sports Fanatic

@gavdraper | +gavdraper | github | StackOverflow Careers
 

AD FS Token Based Authentication In Code

I’m writing this post more as documentation for myself as I know I will be repeating this process quite a lot in coming months.

I’m currently looking at implementing token based security in .Net clients/WCF backend services. This needs a Domain with STS configured in this case I’m using Active Directory Federated Services. There are plenty of examples of doing this via configuration in WCF/ASP.Net MVC, but in this case I wanted a pure code solution in a console app to give me a better idea of how it works and what happens under the hood.

For my starting point I’ve configured 2 VM’s both running Windows Server 2012 with one configured as a domain controller and the other set to be on that domain. If you want details on getting to this position follow this blog post up to the failover clustering header.

Installing AD FS

  1. On the domain controller open the Server Manager
  2. When you get to the screen listing the roles tick “Active Directory Federation Services”
    Picture of Roles in Wizard
  3. Make sure “Include Management Tools” is ticked and click “Add Features”
  4. When you get here tick the following option
    Picture of role services
  5. Click next through the installer until it’s finished.

Configuring AD FS

Before we start the AD FS configuration wizard we need an SSL certificate. AD FS by default tries to use the one on the default site in IIS.

SSL Certificate

  1. Open IIS
  2. Select the server node on the left
  3. Open “Server Certificates” in the features view
    Pictur eOf IIS Features View
  4. Click “Create Self Signed Certificate”
  5. Choose a certificate name and click OK
  6. Double click the certificate you just created
  7. On the details pane click “Copy to file”
  8. Click next
  9. Select “No, do not export the private key” and click next
  10. Leave “DEF encoded binary” selected and click next
  11. Choose a path to save it to and click next then finish (remember this path as you will need it later)
  12. Click the “Default Web Site” node on the left
  13. Click “Bindings” on the right
  14. Click Add
  15. Set the type to https and the SSL certificate to the certificate you just created

AD FS Wizards

  1. From the Start Menu run “AD FS Management”
  2. Then click “AD FS Federation Server Configuration Wizard”
  3. Select “Create a new Federation Service” and click next
  4. Select “Stand-alone federation server” and click next
  5. The certificate we just created should be automatically selected, click next
    Picture of AD FS wizard showing certificate
  6. Click next and wait for the install to finish then click close

Relying Party Configuration

Still in AD FS Management do the following…

  1. Under AD FS/Trust Relationships/Relying Party Trusts click “Add Relying Party Trust…”
  2. Select “Enter data manually” and click next
  3. Choose a display name and click next
  4. Leave “AD FS profile” selected and click next
  5. Leave the certificate blank and click next
  6. Click next without entering a URL
  7. Choose an identifier for this relying party (something like https://adfstest.domain.net/adfs/services/myparty) then click add and next
  8. Leave all users checked and click next
  9. Click next, then finish, then close
  10. You should now have the claims wizard. Click “Add Rule”
  11. Select “Send LDAP Attributes as claims” and click next
  12. Choose a rule name (MyClaims will do)
  13. Set the attribute store to Active Directory
  14. Add a couple of mapped claims, anything that has information in Active Directory will do
    Image of AD FS claims set up
  15. Click finish then OK

Installing The Client Certificates

Assuming you’ve followed along and created a self signed certificate we now need to put that certificate on the client machine to allow our app to communicate with ADFS. We also needs to install the AD FS signing certificate to the client machine so once we have the token we can decrypt it. *If you’ve used trusted certificates you can ignore these steps.

  1. On the AD FS server open “AD FS Management”
  2. Under Service/Certificates double click the Token-signing certificate
  3. On the details tab click “Copy to file” and save the certificate like you did with the last one.
  4. You should now have 2 saved certificates, copy them both to the other VM (The client VM)

On the client VM repeat the following steps for both certificates

  1. Double click the certificate
  2. Click “Install Certificate”
  3. Select “Local Machine” and click next
  4. Select place in following store "and click browse
  5. Select the “Trusted Root Certification Authorities” store and click OK
  6. Click next then finish

Creating a C# Console App To Use AD FS

I used .Net 4.5 and Visual Studio 2012 but I *think* it would be almost the same for .Net3.5 if you’ve installed the Windows Identity Foundation SDK.

  1. In Visual Studio create a new Console App
  2. Add the “Thinktecture.IdentityModel” Nuget package to the project
  3. Change program.cs to look like this
    using System;  
    using System.IO;  
    using System.IdentityModel.Protocols.WSTrust;  
    using System.IdentityModel.Tokens;  
    using System.Linq;  
    using System.Security.Claims;  
    using System.ServiceModel;  
    using System.ServiceModel.Security;  
    using System.Xml;  
    using Thinktecture.IdentityModel.WSTrust;
    
    namespace ConsoleApplication1  
    {
        class Program
        {
            static void Main()
            {
                const string relyingPartyId = "https://adfsserver.security.net/MyApp"; //ID of the relying party in AD FS
                const string adfsEndpoint = "https://adfsserver.security.net/adfs/services/trust/13/windowsmixed";
                const string certSubject = "CN=adfsserver.security.net";
    
                //Setup the connection to ADFS
                var factory = new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(adfsEndpoint))
                {
                    TrustVersion = TrustVersion.WSTrust13
                };
    
                //Setup the request object 
                var rst = new RequestSecurityToken
                {
                    RequestType = RequestTypes.Issue,
                    KeyType = KeyTypes.Bearer,
                    AppliesTo = new EndpointReference(relyingPartyId)
                };
    
                //Open a connection to ADFS and get a token for the logged in user
                var channel = factory.CreateChannel();
                var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
    
                if (genericToken != null)
                {
                    //Setup the handlers needed to convert the generic token to a SAML Token
                    var tokenHandlers = new SecurityTokenHandlerCollection(new SecurityTokenHandler[] { new SamlSecurityTokenHandler() });
                    tokenHandlers.Configuration.AudienceRestriction = new AudienceRestriction();
                    tokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(relyingPartyId));
    
                    var trusted = new TrustedIssuerNameRegistry(certSubject);
                    tokenHandlers.Configuration.IssuerNameRegistry = trusted;
    
                    //convert the generic security token to a saml token
                    var samlToken = tokenHandlers.ReadToken(new XmlTextReader(new StringReader(genericToken.TokenXml.OuterXml)));
    
                    //convert the saml token to a claims principal
                    var claimsPrincipal = new ClaimsPrincipal(tokenHandlers.ValidateToken(samlToken).First());
    
                    //Display token information
                    Console.WriteLine("Name : " + claimsPrincipal.Identity.Name);
                    Console.WriteLine("Auth Type : " + claimsPrincipal.Identity.AuthenticationType);
                    Console.WriteLine("Is Authed : " + claimsPrincipal.Identity.IsAuthenticated);
                    foreach (var c in claimsPrincipal.Claims)
                        Console.WriteLine(c.Type + " / " + c.Value);
                    Console.ReadLine();
                }
            }
    
            //The token handler calls this to check the token is from a trusted issuer before converting it to a claims principal
            //In this case I authenticate this by checking the certificate name used to sign the token
            public class TrustedIssuerNameRegistry : IssuerNameRegistry
            {
                private string _certSubject;
    
                public TrustedIssuerNameRegistry(string certSubject)
                {
                    _certSubject = certSubject;
                }
    
                public override string GetIssuerName(SecurityToken securityToken)
                {
                    var x509Token = securityToken as X509SecurityToken;
                    if (x509Token != null && 
                        x509Token.Certificate.SubjectName.Name!=null &&
                        x509Token.Certificate.SubjectName.Name.Contains(_certSubject))
                        return x509Token.Certificate.SubjectName.Name;
                    throw new SecurityTokenException("Untrusted issuer.");
                }
            }
    
        }
    }
    
  4. The top 3 lines need to be changed to match your AD FS setup.

For the relying party id enter the id you choose when creating the relying party.

For the adfsEndpoint in AD FS Management expand endpoints, make sure the line that contains “/trust/13/windowsmixed” is set to enabled and use that path with the server path to get the endpoint.

The cert subject needs the subject of your signing certificate, if you're not sure what this is put a breakpoint in the GetIssuerName method and have a look at what the subject on the certificate object is set to.

Assuming everything is set up correctly you should now be able to run the console application and get back your user details from AD FS With its associated claims.

Image showing claims in a console app

On a closing point I highly recommend Dominick Baier’s blog for more on the subject of Windows Identity Foundation and Secure Token Services. He answered a number of my questions on stackoverflow whilst I was working on this and really knows his stuff.


 
comments powered by Disqus