Exercise 3: Securing a WCF service using Windows Azure Active Directory


Exercise 3: Securing a WCF service using Windows Azure Active Directory

Rate This
  • Comments 13

In this exercise, we’ll secure our WCF service using Windows Azure Active Directory.


The following is required to complete this hands-on lab:

  • Microsoft Visual Studio 2013
  • The latest Windows Azure SDK for .NET
  • A Windows Azure subscription

Download the finished WPF Client and WCF Service

Previous Labs

The following is required to complete this hands-on lab:

Task 1: Creating a Windows Azure Active Directory tenant and add a user

In this task, we’ll create a Windows Azure Active Directory tenant. We’ll also add a user, which is specific to this lab. In a real environment, we would set up Active Directory synchronization so that our Azure AD instance relied on the users pushed from the on-premises instance.

  1. Log into your Azure administrative account at https://manage.windowsazure.com if not already open.

  2. In the bottom right corner, click the New button.


    Adding Azure Directory Services

  3. You will create a new active directory in the Windows Azure portal. From the portal menu, choose App Services, active directory, directory.


    Creating a new active directory

  4. You will select custom create.


    Creating a new active directory

  5. You will specify the new directory details. Type in name, domain name, and a country or region.


    Specifying directory details

  6. You will validate the newly created directory. Verify the newly created directory is visible.


    Validating the creation of a new active directory

  7. You will now add users to the new active directory. From the menu choose users.


    Adding new users

  8. You will now add a new user. From the bottom menu bar choose add user.


    Adding a new user

  9. You will enter user details. Choose new user for the type of user. Enter a user name.


    Entering user details

  10. You will complete the entering of user information. Specify the first, last, and display name. This will be an ordinary user so from them role drop-down choose user.


    Specifying user details

  11. You will now receive a temporary password. Click the create button.


    Creating a temporary password

  12. You will receive a random password. It will also type in an email address which will contain the generated password.


    Creating a temporary password

Task 2: Creating a Windows Azure Active Directory service application

  1. You will now associate an application with the directory services you just created. From your directory services detail pane choose applications then add an app.


    Adding an application for your directory services

  2. From the menu choose add an application my organization is developing.


    Adding an organizational application

  3. Provide a name for your application. Verify that the type is a web application or web API application.


    Naming your application

  4. You will verify your application properties. Note the sign-on URL as well as the app ID URI.


    Validating application properties

  5. You will enable users to sign into your application. Choose enable users to sign on.


    Enabling sign-on for users

  6. You will copy the app ID URI to the clipboard. Also take note of the Federation meta data document URL.


    Collecting application information from the portal

Task 2: Modifying the WCF Service

You will return back to the WCF service created in the previous steps. Start Visual Studio as administrator and open the project. The download link is provided at the beginning of this post.

  1. You will now add a class to help manage the tokens in your application. Right mouse click on App_Code, then add, followed by class. Name the class module BearerTokenMessageInspector.cs. Also note the code snippet below.


    Adding a new class

  2. Open the class module just created. Paste in the code from the code snippet.


    Pasting in the code for BearerTokenMessageInspector.cs

    (Code Snippet - BearerTokenMessageInspector.cs)

    // BearerTokenMessageInspector.cs 
    // Windows Azure Active Directory Helper
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IdentityModel.Metadata;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Services;
    using System.IdentityModel.Tokens;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Claims;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Security;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    using System.Xml;
    namespace Expenses.WcfService
         public class BearerTokenMessageInspector : IDispatchMessageInspector
              // You need to use what you entered
              // at the portal. You audience and authority
              // will be different.
              const string audience = "http://brunoexpenseswcf.azurewebsites.net";
              const string authority = "https://login.windows.net/expensesdomain.onmicrosoft.com";
              static string _issuer = string.Empty;
              static List<X509SecurityToken> _signingTokens = null;
              static DateTime _stsMetadataRetrievalTime = DateTime.MinValue;
              static string scopeClaimType = "http://schemas.microsoft.com/identity/claims/scope";
              public BearerTokenMessageInspector()
              public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
                    object correlationState = null;
                    HttpRequestMessageProperty requestMessage = request.Properties["httpRequest"] as HttpRequestMessageProperty;
                    if (request == null)
                         throw new InvalidOperationException("Invalid request type.");
                    string authHeader = requestMessage.Headers["Authorization"];
                    if (string.IsNullOrEmpty(authHeader) || !this.Authenticate(authHeader))
                         WcfErrorResponseData error = new WcfErrorResponseData(HttpStatusCode.Unauthorized, string.Empty, new KeyValuePair<string, string>("WWW-Authenticate", "Bearer authorization_uri=\"" + authority + "\"" + "," + "resource_id=" + audience));
                         correlationState = error;
                    return correlationState;
              private bool Authenticate(string authHeader)
                    const string bearer = "Bearer ";
                    if (!authHeader.StartsWith(bearer, StringComparison.InvariantCultureIgnoreCase)) { return false; }
                    string jwtToken = authHeader.Substring(bearer.Length);
                    string issuer;
                    string stsMetadataAddress = string.Format("{0}/federationmetadata/2007-06/federationmetadata.xml", authority);
                    List<X509SecurityToken> signingTokens;
                    // Get tenant information that's used to validate incoming jwt tokens
                    GetTenantInformation(stsMetadataAddress, out issuer, out signingTokens);
                    JwtSecurityTokenHandler tokenHandler =
                         new JwtSecurityTokenHandler()
                              // For demo purposes certificate validation is turned off. Please note that this shouldn't be done in production code.
                              CertificateValidator = X509CertificateValidator.None
                    TokenValidationParameters validationParameters =
                         new TokenValidationParameters()
                              //AllowedAudience = audience,
                              ValidIssuer = issuer
                              //SigningTokens = signingTokens,
                    // Validate token
                    ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters);
                    // Set the ClaimsPrincipal on the current thread.
                    Thread.CurrentPrincipal = claimsPrincipal;
                    // Set the ClaimsPrincipal on HttpContext.Current if the app is running in web hosted environment.
                    if (HttpContext.Current != null)
                         HttpContext.Current.User = claimsPrincipal;
                    // if the token is scoped, verify that required permission is set in the scope claim
                    if ((ClaimsPrincipal.Current.FindFirst(scopeClaimType) != null) && (ClaimsPrincipal.Current.FindFirst(scopeClaimType).Value != "user_impersonation"))
                         return false;
                    return true;
              /// <summary>
              /// Parses the federation metadata document and gets issuer Name and Signing Certificates
              /// </summary>
              /// <param name="metadataAddress">URL of the Federation Metadata document</param>
              /// <param name="issuer">Issuer Name</param>
              /// <param name="signingTokens">Signing Certificates in the form of X509SecurityToken</param>
              static void GetTenantInformation(string metadataAddress, out string issuer, out List<X509SecurityToken> signingTokens)
                    signingTokens = new List<X509SecurityToken>();
                    // The issuer and signingTokens are cached for 24 hours. They are updated if any of the conditions in the if condition is true.            
                    if ((DateTime.UtcNow.Subtract(_stsMetadataRetrievalTime).TotalHours > 24)
                         || string.IsNullOrEmpty(_issuer)
                         || _signingTokens == null)
                         MetadataSerializer serializer = new MetadataSerializer()
                              // turning off certificate validation for demo. Don't use this in production code.
                              CertificateValidationMode = X509CertificateValidationMode.None
                         MetadataBase metadata = serializer.ReadMetadata(XmlReader.Create(metadataAddress));
                         EntityDescriptor entityDescriptor = (EntityDescriptor)metadata;
                         // get the issuer name
                         if (!string.IsNullOrWhiteSpace(entityDescriptor.EntityId.Id))
                              _issuer = entityDescriptor.EntityId.Id;
                         // get the signing certs
                         _signingTokens = ReadSigningCertsFromMetadata(entityDescriptor);
                         _stsMetadataRetrievalTime = DateTime.UtcNow;
                    issuer = _issuer;
                    signingTokens = _signingTokens;
              static List<X509SecurityToken> ReadSigningCertsFromMetadata(EntityDescriptor entityDescriptor)
                    List<X509SecurityToken> stsSigningTokens = new List<X509SecurityToken>();
                    SecurityTokenServiceDescriptor stsd = entityDescriptor.RoleDescriptors.OfType<SecurityTokenServiceDescriptor>().First();
                    if (stsd != null)
                         IEnumerable<X509RawDataKeyIdentifierClause> x509DataClauses = stsd.Keys.Where(key => key.KeyInfo != null && (key.Use == KeyType.Signing || key.Use == KeyType.Unspecified)).
                                                                                     Select(key => key.KeyInfo.OfType<X509RawDataKeyIdentifierClause>().First());
                         stsSigningTokens.AddRange(x509DataClauses.Select(token => new X509SecurityToken(new X509Certificate2(token.GetX509RawData()))));
                         throw new InvalidOperationException("There is no RoleDescriptor of type SecurityTokenServiceType in the metadata");
                    return stsSigningTokens;
              public void BeforeSendReply(ref Message reply, object correlationState)
                    WcfErrorResponseData error = correlationState as WcfErrorResponseData;
                    if (error != null)
                         HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
                         reply.Properties["httpResponse"] = responseProperty;
                         responseProperty.StatusCode = error.StatusCode;
                         IList<KeyValuePair<string, string>> headers = error.Headers;
                         if (headers != null)
                              for (int i = 0; i < headers.Count; i++)
                                    responseProperty.Headers.Add(headers[i].Key, headers[i].Value);
         public class BearerTokenServiceBehavior : IServiceBehavior
              public BearerTokenServiceBehavior()
              public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
                    // no-op
              public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                    foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
                         foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
                              epDisp.DispatchRuntime.MessageInspectors.Add(new BearerTokenMessageInspector());
              public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                    // no-op
         public class BearerTokenExtensionElement : BehaviorExtensionElement
              public override Type BehaviorType
                    get { return typeof(BearerTokenServiceBehavior); }
              protected override object CreateBehavior()
                    return new BearerTokenServiceBehavior();
         internal class WcfErrorResponseData
              public WcfErrorResponseData(HttpStatusCode status) :
                    this(status, string.Empty, new KeyValuePair<string, string>[0])
              public WcfErrorResponseData(HttpStatusCode status, string body) :
                    this(status, body, new KeyValuePair<string, string>[0])
              public WcfErrorResponseData(HttpStatusCode status, string body, params KeyValuePair<string, string>[] headers)
                    StatusCode = status;
                    Body = body;
                    Headers = headers;
              public HttpStatusCode StatusCode
                    private set;
              public string Body
                    private set;
              public IList<KeyValuePair<string, string>> Headers
                    private set;

    Copying all the code in BearerTokenMessageInspector.cs

  3. You will modify the audience and authority strings to reflect the information from the portal. Your URLs will differ.


    Modifying BearerTokenMessageInspector.cs with information from the portal

  4. You will now enable Javascript web tokens. From the tools menu choose library package manager, then package manager console.


    Starting package manager console

  5. Type in the following command into package manager console. Take special note that this is version 1.

    Install-Package System.IdentityModel.Tokens.Jwt -version 1.0.0


    Installing the identity model package for JavaScript web tokens version 1

  6. You will need to add references. Right mouse click on the references and choose add reference.


    Adding references

  7. You will add two references. The first reference is seen below. Make sure it is checked. Click OK to continue.


    Adding a reference

  8. You will now add a second reference as seen below.


    Adding a reference

  9. Open the web.config file and add the following entries. Notice that we have both behavior and behavior extensions.


    Modifying web.config to implement authentication

    (Code Snippet - Web.config in Expeneses.WCFServcie)

    <?xml version="1.0"?>
            connectionString="Server=tcp:uxpwgfs4g6.database.windows.net,1433;Database=Expenses;User ID=azureuser@uxpwgfs4g6;Password=MyP@ssw0rd;       Trusted_Connection=False;Encrypt=True;Connection Timeout=30;"
         <customErrors mode="Off"/>
              <add assembly="System.Security, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
              <add assembly="System.Data.Linq, Version=, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
              <add assembly="System.IdentityModel.Services, Version=, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
              <add assembly="System.Net.Http, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
         <httpRuntime targetFramework="4.5"/>
                 <!-- Add this line below. Ignore errors ->
                 <!--To avoid disclosing metadata information, set the values below to false before deployment -->
                 <!--To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
                 <serviceDebug includeExceptionDetailInFaults="true"/>
          <!-- Add this extensions section below. ->
                 type="Expenses.WcfService.BearerTokenExtensionElement, App_Code"/>
         <modules runAllManagedModulesForAllRequests="true"/>
         <!--To browse web app root directory during debugging, set the value below to true.
         Set to false before deployment to avoid disclosing web app folder information.
         <directoryBrowse enabled="true"/>
  10. You are now ready to publish the updated WCF Service. Right mouse click as seen below and choose publish website.


    Publishing the website (the WCF service)

  11. Publish the service. Click the publish button.


    Publishing the service

  12. View the output window to verify correct publishing.


    Verifying the publish

Task 3: Running the WPF client to test the service

You will return back to the WPF client app from previous steps. Start Visual Studio as administrator and open the project. The download link for this project is provided at the beginning of this post.

  1. Return back to the WPF application (client app) to verify that the service is no longer available without a proper login and authentication.


    Testing the WCF service by running the WPF application

  2. You will notice that now we have a runtime error for the WPF application. We will need to make more modifications to avoid the failed attempt to connect to the WCF service.


    Running into runtime errors


Are many things going on with this lab.

  • You provisioned a new directory services from the Azure portal

  • You added a user to the directory services

  • You associated the WCF service with the directory services and the portal

  • You added some authentication code and the WCF service

  • You re-published the service back up to Azure websites

  • You re-tested the WPF client to verify it could not access the services because it lacks proper authentication

  • So why didn't you specify what modifications have to be made on the client now? I can not find that information anywhere. The only link I came across regarding that is dead:


  • Ivan, See the next post:


  • I have my WCF service setup and working. Now I am trying to get it onboarded with AAD. So I came across this article.

    I created the App_Code folder [as it was not present in WCF webrole by default] and inside that I created the BearerTokenMessageInspector.cs.

    WCFMockService is my webrole name

    MockService is my service.

    My webconfig has :





             <serviceMetadata httpsGetEnabled="true"/>

             <serviceDebug includeExceptionDetailInFaults="true"/>






           <add name="bearerTokenRequired"

              type="WCFMockService.MockService.BearerTokenExtensionElement, App_Code"/>



    I am getting the following error after publishing the service.

    The type 'WCFMockService.MockService.BearerTokenExtensionElement, App_Code' registered for extension 'bearerTokenRequired' could not be loaded.

    I even tried changing the extension to



           <add name="bearerTokenRequired"

              type="WCFMockService.MockService.BearerTokenExtensionElement, App_Code, Version=, Culture=neutral, PublicKeyToken=null"/>


    Still doesn't work. Please Help.

    Thanks in advance.

  • On more troubleshooting, I found that even though I have System.IdentityModel.Services and System.Net.Http referenced and showing in References list, still they are not found and marked red in BearerTokenMessageInspector.cs.


    - "Cannot resolve symbol Services" and

    - "Cannot resolve symbol Http"

  • Resolved the above errors for references.

    But still getting the initial error.

    The type 'WCFMockService.MockService.BearerTokenExtensionElement, App_Code' registered for extension 'bearerTokenRequired' could not be loaded.

  • I provide the entire source code for client and server that ran successfully on my machine. Unfortunately, I am unable to debug code for my readers. I apologize that this not something I cannot support due to limited time.

  • I understand. No Problem.

    I am confused because my service gets created if I run the WCF service web role on local machine but when I publish to azure, it gives the error for extension not found.

  • Fixed the error on Azure as well.

    But now when I try to add reference to WCF service, I get a pop up window to enter username and password? and if I don't enter credentials, it is not allowing me to reference service.

    Which credentials to enter here? Did you got same window in your process?

  • I am getting same issue like Jason.

    I am done till making changes to service but now on client, when I am trying to reference the service, I get a window for entering credentials. I have entered various kind of credentials but nothing works.

    If I remove  <bearerTokenRequired/> from web.config then I can reference the service and it doesn't ask for credentials but that also, means the service is not behind AAD anymore.

    So, need your guidance on what should I remove from the your given code that keeps the service behind AAD but doesn't prompt credentials on referencing to the service.

    I feel the reference to service should not be restricted by Azure AD. If my understanding is not correct, then what credentials we should enter to successfully reference the service?

    Please help.

  • Hi,Jason how did u fix your above mentioned error? I am getting a similar error

  • Bruno,

    Would it be possible to update this using the latest libraries and using a windows8 store app as the client?


  • Nice work Bruno.  Is there an example for a java client ?

  • I'm pretty sure what has happened for a number of people above is that publish to Azure skips the App_Code directory (and its contents) when deploying to an Azure Web Site. I had the same bearerTokenRequired issue but when I looked at my site with WebMatrix the App_Code directory was missing. I added it manually after which I receive an expected 401 response.

Page 1 of 1 (13 items)
Leave a Comment
  • Please add 3 and 5 and type the answer here:
  • Post