Home > Azure > Securing Web API using Azure Part – 3: Building the solution

Securing Web API using Azure Part – 3: Building the solution

Continuing with my Securing Web API series in this post I am going to start setting up the solution in visual studio and start building up the Simple Web Token Library. The idea behind this library is to build a reusable library which can be used with Azure ACS but can easily be extended for any identity provider which provides a simple web token based security.

In the first part we learned how to configure simple web tokens using Azure and in the second part we looked into designing interfaces which gives us a nice separation of concern.

Just to recap…

Securing Web API using Azure Part – 1: Simple Web Tokens (Azure Configuration)

Securing Web API using Azure Part – 2: Designing Interfaces (Web Token Orchestration)

So as usual I will start with File > New > ASP.Net Web Application and then select ASP.NET Web Api as shown below.

File new project web api

and now I am going to add a new class library “SimpleWebTokenLibrary” to the solution and create some folders as shown below.

Solution Structure

The structure is pretty straight forward and most of the interfaces, web token constant class, I have already covered in my previous post. The only interface which I haven’t covered is the ISettings interface which allows me to decouple the dependency on app config or web config. This may seems like a simple thing but it helps a lot when you are doing a lot of testing and don’t have to copy around your app settings into your test project.

ISettings.cs

public interface ISettings
{
    string GetSettings(string key);
}

and the implementation class just acts as a facade to the .net configuration manager class.

public class AppSettingsProvider : ISettings
{
    public string GetSettings(string key)
    {
        return ConfigurationManager.AppSettings[key];
    }
}

Also to avoid passing magic string for config settings key I am going to encapsulate that into a settings name class for better readability of code and reducing the magic string code smell.

SettingNames.cs

public static class SettingNames
{
    public const string ServiceNamespace = "ServiceNamespace";

    public const string IdpHostName = "IdpHostName";

    public const string TrustedTokenPolicyKey = "TrustedTokenPolicyKey";

    public const string TrustedAudience = "TrustedAudience";

    public const string ServiceUserId = "ServiceUserId";

    public const string ServiceUserPassword = "ServiceUserPassword";

    public const string RealmScope = "RealmScope";
}

Now I am going to create another class in the Common folder call ResponseManager which will help generating the appropriate http response messages and the http header based on different scenarios.

ResponseManager.cs

public class ResponseManager
{
    public static Task<HttpResponseMessage> GenerateTaskResponse(
                    string message, 
                    CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(
                () => new HttpResponseMessage(HttpStatusCode.Unauthorized) 
                { 
                    Content = new StringContent(message) 
                },cancellationToken);
    }
}

And to encapsulate identity provider settings like host name, service namespace, relying application etc. I am going to create the following class.

IdentityProviderSettings.cs

public class IdentityProviderSettings
{
    public IdentityProviderSettings(ISettings settings)
    {
        IdpHostName = settings.GetSettings(SettingNames.IdpHostName);
        ServiceNamespace = settings.GetSettings(SettingNames.ServiceNamespace);
        TrustedAudienceValue = settings.GetSettings(SettingNames.TrustedAudience);
        TrustedSigningKey = settings.GetSettings(SettingNames.TrustedTokenPolicyKey);
    }

    public string TrustedAudienceValue { get; set; }

    public string TrustedSigningKey { get; set; }

    public string IdpHostName { get; set; }

    public string ServiceNamespace { get; set; }

    public string TrustedTokenIssuer
    {
        get
        {
            return string.Format(WebTokenConstant.UrlFormat, 
                                this.ServiceNamespace.ToLowerInvariant(), 
                                this.IdpHostName.ToLowerInvariant());
        }
    }
}

So now we are all done with our basic classes lets look into the WebTokenProvider class whose responsibility is to use the service username and password to build and OAuth request and retrieve a token which contains information like the encrypted token, relying party, identity provider etc.

WebTokenProvider.cs

public class WebTokenProvider : IWebTokenProvider
{
    private readonly ISettings settings;

    public WebTokenProvider(ISettings settings)
    {
        this.settings = settings;
    }

    public string GenerateToken()
    {
        var wrapPassword = settings.GetSettings(SettingNames.ServiceUserPassword);
        var wrapUsername = settings.GetSettings(SettingNames.ServiceUserId);
        var scope = settings.GetSettings(SettingNames.RealmScope);

        var client = new WebClient
        {
            BaseAddress = string.Format(
                            WebTokenConstant.UrlFormat,
                            settings.GetSettings(SettingNames.ServiceNamespace),
                            settings.GetSettings(SettingNames.IdpHostName))
        };

        var values = new NameValueCollection
        {
            { WebTokenConstant.WrapName, wrapUsername },
            { WebTokenConstant.WrapPassword, wrapPassword },
            { WebTokenConstant.WrapScope, scope }
        };

        var responseBytes = client.UploadValues(
                                    WebTokenConstant.WrapVersion09,
                                    WebTokenConstant.Post, values);

        var response = Encoding.UTF8.GetString(responseBytes);

        return HttpUtility.UrlDecode(
            response.Split('&')
            .Single(value => value.StartsWith(
                                    WebTokenConstant.WrapAccessToken,
                                    StringComparison.OrdinalIgnoreCase))
            .Split('=')[1]);
    }
}

The above implementation looks very straight forward and quite neat. As you can see all the extra effort we put around constants and removing magic string code smell paid off and it is quite easy to read and understand.

Now lets look into token validator class.

TokenValidator.cs

public class TokenValidator : ITokenValidator
{
    private readonly string trustedSigningKey;

    private readonly string trustedTokenIssuer;

    private readonly string trustedAudienceValue;

    public TokenValidator(IdentityProviderSettings identityProviderSettings)
    {
        trustedSigningKey = identityProviderSettings.TrustedSigningKey;
        trustedTokenIssuer = identityProviderSettings.TrustedTokenIssuer;
        trustedAudienceValue = identityProviderSettings.TrustedAudienceValue;
    }

    public bool Validate(string token)
    {
        var isExpired = this.IsExpired(token);

        return IsHMACValid(token, Convert.FromBase64String(this.trustedSigningKey))
               && (!this.IsExpired(token)
               && (this.IsIssuerTrusted(token)
               && this.IsAudienceTrusted(token)));
    }

    public Dictionary<string, string> GetNameValues(string token)
    {
        if (string.IsNullOrEmpty(token))
        {
            throw new ArgumentException();
        }

        return token.Split('&').Aggregate(
            new Dictionary<string, string>(),
            (dict, rawNameValue) =>
            {
                if (rawNameValue == string.Empty)
                {
                    return dict;
                }

                var nameValue = rawNameValue.Split('=');

                if (nameValue.Length != 2)
                {
                    throw new ArgumentException(
                                WebTokenConstant.InvalidFormEncoding);
                }

                if (dict.ContainsKey(nameValue[0]))
                {
                    throw new ArgumentException(
                                WebTokenConstant.DuplicateNameValuePair);
                }

                dict.Add(HttpUtility.UrlDecode(
                                        nameValue[0]), 
                                        HttpUtility.UrlDecode(nameValue[1]));
                return dict;
            });
    }

    private static ulong GenerateTimeStamp()
    {
        var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        return Convert.ToUInt64(ts.TotalSeconds);
    }

    private bool IsAudienceTrusted(string token)
    {
        var tokenValues = this.GetNameValues(token);

        string audienceValue;

        tokenValues.TryGetValue(
                        WebTokenConstant.AudienceLabel, 
                        out audienceValue);

        return !string.IsNullOrEmpty(audienceValue) && 
                            audienceValue.Equals(
                                this.trustedAudienceValue, 
                                StringComparison.Ordinal);
    }

    private bool IsIssuerTrusted(string token)
    {
        var tokenValues = this.GetNameValues(token);

        string issuerName;

        tokenValues.TryGetValue(WebTokenConstant.IssuerLabel, out issuerName);

        if (string.IsNullOrEmpty(issuerName))
        {
            return false;
        }

        return issuerName.Equals(this.trustedTokenIssuer);
    }

    public static bool IsHMACValid(string swt, byte[] sha256HMACKey)
    {
        var swtWithSignature = swt.Split(new[] { "&" + WebTokenConstant.HmacSha256Label + "=" }, 
                                                    StringSplitOptions.None);

        if (swtWithSignature.Length != 2)
        {
            return false;
        }

        var hmac = new HMACSHA256(sha256HMACKey);

        var locallyGeneratedSignatureInBytes = hmac.ComputeHash(
                                              Encoding.ASCII.GetBytes(swtWithSignature[0]));

        var locallyGeneratedSignature = HttpUtility.UrlEncode(
                                        Convert.ToBase64String(locallyGeneratedSignatureInBytes));

        return locallyGeneratedSignature == swtWithSignature[1];
    }

    private bool IsExpired(string swt)
    {
        try
        {
            var nameValues = this.GetNameValues(swt);
            var expiresOnValue = nameValues[WebTokenConstant.ExpiresLabel];
            var expiresOn = Convert.ToUInt64(expiresOnValue);
            var currentTime = Convert.ToUInt64(GenerateTimeStamp());

            return currentTime > expiresOn;
        }
        catch (KeyNotFoundException)
        {
            throw new ArgumentException();
        }
    }
}

As you can see the above code is an anatomy of simple web tokens and we are parsing the return web token to retrieve information like whether the issuer is trusted or not, whether the encrypted token and the signing key matches or not etc.

Finally the TokenHeaderValidiator class.

TokenHeaderValdiator.cs

public class TokenHeaderValidator : ITokenHeaderValidator
{
    public bool Validate(HttpRequestMessage request, 
                         CancellationToken cancellationToken, 
                         out string headerValue, 
                         out string[] nameValuePair, 
                         out Task<HttpResponseMessage> taskHttpResponseMessage)
    {
        nameValuePair = new string[] { };
        headerValue = request.Headers
                             .GetValues(WebTokenConstant.Authorization)
                             .First();

        if (string.IsNullOrEmpty(headerValue))
        {
            taskHttpResponseMessage = ResponseManager.GenerateTaskResponse(
                                        WebTokenConstant.AuthorizationHeaderIsEmpty, 
                                        cancellationToken);
            return false;
        }

        if (!headerValue.StartsWith(WebTokenConstant.AuthorizationProtocol))
        {
            taskHttpResponseMessage = ResponseManager.GenerateTaskResponse(
                                        WebTokenConstant.InvalidToken, 
                                        cancellationToken);
            return false;
        }

        nameValuePair = headerValue.Substring(WebTokenConstant.AuthorizationProtocol.Length)
                                   .Split(new[] { '=' }, 2);

        if (nameValuePair.Length != 2 || nameValuePair[0] != WebTokenConstant.AccessToken || 
            !nameValuePair[1].StartsWith("\"") || !nameValuePair[1].EndsWith("\""))
        {
            throw new ApplicationException(WebTokenConstant.Unauthorized);
        }

        taskHttpResponseMessage = null;
        return true;
    }
}

The two classes TokenValidator and TokenHeaderValidator will be injected into the TokenValidationHandler class which will sit in the ASP.NET Web API pipeline and will make sure that the token is authentic and everything is right before passing the request to the ApiController. If you are not sure how this all fits together then please have a look at SWT Token Orchestration diagram I provided in the previous post..

I know it’s a lot of code but I hope to complete the solution in my next post. I’ll also upload the whole solution with some test to my skydrive so that it is easy to understand the code and relate the concepts in a more manageable way.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: