Basic Authentication...Almost

Topics: Web Api
Jun 1, 2011 at 11:40 PM

One of my tasks for today was to get Basic Auth up and running. I accomplished it, with one exception. I'm not sure where to put the IPrinciple instance. Have a look at this code:

public class BasicAuthenticationMessageHandler : DelegatingChannel {
    readonly IUserValidator userValidator;
    readonly string realm;

    public BasicAuthenticationMessageHandler(HttpMessageChannel innerChannel, IUserValidator userValidator, string realm)
        : base(innerChannel) {
        if(userValidator == null)
            throw new ArgumentNullException("userValidator");

        if(string.IsNullOrEmpty(realm))
            throw new ArgumentNullException("realm");

        this.userValidator = userValidator;
        this.realm = realm;
    }

    public string Scheme {
        get { return "Basic"; }
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
        var credentials = ExtractCredentials(request);
        IPrincipal principal;

        if(credentials.Length == 0 || !userValidator.Validate(credentials[0], credentials[1], out principal)) {
            return Task.Factory.StartNew(() => {
                var response = new HttpResponseMessage(HttpStatusCode.Unauthorized, "Access Denied");
                response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(Scheme, "realm=" + realm));
                return response;
            });
        }

        InitializeSecurityContext(request, principal);

        return base.SendAsync(request, cancellationToken);
    }

    void InitializeSecurityContext(HttpRequestMessage request, IPrincipal principal) {}

    string[] ExtractCredentials(HttpRequestMessage request) {
        if(request.Headers.Authorization != null && request.Headers.Authorization.Scheme.StartsWith(Scheme)) {
            var encodedUserPass = request.Headers.Authorization.Parameter.Trim();

            try {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                var userPass = encoding.GetString(Convert.FromBase64String(encodedUserPass));
                var separator = userPass.IndexOf(':');

                var credentials = new string[2];
                credentials[0] = userPass.Substring(0, separator);
                credentials[1] = userPass.Substring(separator + 1);

                return credentials;
            }
            catch(FormatException) {
                return new string[] { };
            }
        }

        return new string[] { };
    }
}

How should I implement the InitializeSecurityContext method? Where should I be putting the instance of IPrinciple?

Jun 2, 2011 at 9:04 AM

Perhaps as a HttpRequestMessage property.

Then, if needed, you can have an operation handler that extracts the IPrincipal and adds it to the operation parameters or evaluates an authorization policy based on it.

Pedro