ASP.NET Web API Security Essentials
上QQ阅读APP看书,第一时间看更新

Implementing authentication in HTTP message handlers

For a self-hosted web API, the best practice is to implement authentication in an HTTP Message Handler. The principal will be set by the message handler after verifying the HTTP request. For a web API that is self-hosted, consider implementing authentication in a message handler. Otherwise, use an HTTP module instead.

The following code snippet shows an example of basic authentication implemented in an HTTP module:

public class AuthenticationHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                               CancellationToken cancellationToken)
        {
            var credentials = ParseAuthorizationHeader(request);

            if (credentials != null)
            {
                // Check if the username and passowrd in credentials are valid against the ASP.NET membership.
                // If valid, the set the current principal in the request context
                var identity = new GenericIdentity(credentials.Username);
                Thread.CurrentPrincipal = new GenericPrincipal(identity, null);;
            }

            return base.SendAsync(request, cancellationToken)
                .ContinueWith(task =>
                {
                    var response = task.Result;
                    if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized)
                        Challenge(request, response);

                    return response;
                });
        }

        protected virtual Credentials ParseAuthorizationHeader(HttpRequestMessage request)
        {
            string authorizationHeader = null;
            var authorization = request.Headers.Authorization;
            if (authorization != null && authorization.Scheme == "Basic")
                authorizationHeader = authorization.Parameter;

            if (string.IsNullOrEmpty(authorizationHeader))
                return null;

            authorizationHeader = Encoding.Default.GetString(Convert.FromBase64String(authorizationHeader));

            var authenticationTokens = authorizationHeader.Split(':');
            if (authenticationTokens.Length < 2)
                return null;

            return new Credentials() { Username = authenticationTokens[0], Password = authenticationTokens[1], };
        }

        void Challenge(HttpRequestMessage request, HttpResponseMessage response)
        {
            response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", request.RequestUri.DnsSafeHost));
        }

        public class Credentials
        {
            public string Username { get; set; }
            public string Password { get; set; }
        }
    }