为Azure应用服务WebApi OWIN启用基本身份验证

时间:2015-11-27 13:27:55

标签: asp.net-web-api azure-web-sites iis-8 http-basic-authentication

我有一个ASP.Net 4.5.2 MVC5 WebApi 5.2.3应用程序,它通常通过OWIN管道使用OAuth / Jwt安全性。现在,我有一个外部服务Web挂钩,通过POST调用我的一个WebApi控制器。此webhook服务仅支持基于Https的基本身份验证。这很好,如果我只能让我的过滤器(或其他)工作。

我的过滤器基于一个很好的example by Rick Strahl然而,当我在我的ApiController Action上面添加这个类然后我的[MyBasicAuthFilter]时,我得到的是一条服务器错误500消息{{Message“:”An错误已经发生。“}。

当我从POST调用中删除“Authorization:Basic dXNlcasdfasfdsfasd =”标题时,代码会输入我的新Filter。哦,太好了 :-[。因此,pipleline中较早的内容并不喜欢此标头存在的事实。我读了一下后怀疑是IIS设置。但是在尝试通过我的Web.Config胁迫Azure App Service IIS并立即阻止对我网站的所有访问之后,我想我会来这里寻求一些提示。

思想/提示?

1 个答案:

答案 0 :(得分:0)

好的,这是关于这里发生了什么的独家新闻。现有的JsonWebTokenValidationHandler(消息处理程序)只是在寻找一个" Bearer"标题中的标记。如果出现任何其他令牌,那么它会将其标记为潜在攻击并返回HttpStatus 500.如果头部中根本没有令牌,那么它将返回挑战HttpStatus 401.无论如何,我的简约BasicAuth要求的最佳解决方案(在JWT令牌之上)是我的JsonWebTokenValidationHandler.cs的以下(完整)内容。这现在处理Bearer(JWT)令牌和Basic Auth令牌。欢迎评论。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

public class JsonWebTokenValidationHandler : DelegatingHandler
{
    public string SymmetricKey { get; set; } // A0 client secret

    public string Audience { get; set; } // A0 client ID

    public string Issuer { get; set; } // A0 our A0 domain

    private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authzHeaders;

        if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
        {
            // Fail if no Authorization header or more than one Authorization headers  
            // are found in the HTTP request  
            return false;
        }

        // Remove the bearer token scheme prefix and return the rest as ACS token  
        var bearerToken = authzHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;

        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        string token;
        HttpResponseMessage errorResponse = null;

        if (TryRetrieveToken(request, out token))
        {
            try
            {
                // ++
                // Added BasicAuth to our JWT Auth
                // If it is a BasicAuth token, then check it first and bypass JWT validation
                // See Asp.Net WebApi security book for infos on this.
                //
                if (token.Contains("Basic"))
                {
                    var authHeader = Encoding.Default.GetString(Convert.FromBase64String(token.Substring(6)));

                    // find first : as password allows for :
                    int idx = authHeader.IndexOf(':');
                    if (idx < 0)
                        return null;

                    string username = authHeader.Substring(0, idx);
                    string password = authHeader.Substring(idx + 1);

                    // Check the user's validity and reuse the JWT challenge 401 exception if not valid.
                    if (!ValidateBasicAuthUser(username, password))
                    {
                        throw new JsonWebToken.TokenValidationException(
                            $"Username or password missmatch for username: {username}");
                    }

                    // If OK, then let them in and continue
                    var identity = new BasicAuthenticationIdentity(username, password);
                    var principal = new GenericPrincipal(identity, null);

                    Thread.CurrentPrincipal = principal;
                    if (HttpContext.Current != null)
                    {
                        HttpContext.Current.User = Thread.CurrentPrincipal;
                    }
                }
                else
                {
                    // --

                    // Handle JWT Tokens here or return server error 500 if it is not a valid token.
                    var secret = this.SymmetricKey.Replace('-', '+').Replace('_', '/');

                    Thread.CurrentPrincipal = JsonWebToken.ValidateToken(
                        token,
                        secret,
                        audience: this.Audience,
                        checkExpiration: true,
                        issuer: this.Issuer);

                    if (HttpContext.Current != null)
                    {
                        HttpContext.Current.User = Thread.CurrentPrincipal;
                    }
                }
            }
            catch (JWT.SignatureVerificationException ex)
            {
                errorResponse = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex);
            }
            catch (JsonWebToken.TokenValidationException ex)
            {
                errorResponse = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex);
            }
            catch (Exception ex)
            {
                errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
            }
        }

        return errorResponse != null ?
            Task.FromResult(errorResponse) :
            base.SendAsync(request, cancellationToken);
    }

    /// <summary>
    /// rht: Check our BasicAuth passwords e.g. from Cb WebHook
    /// Usage: http://weblog.west-wind.com/posts/2013/Apr/18/A-WebAPI-Basic-Authentication-Authorization-Filter
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    private bool ValidateBasicAuthUser(string username, string password)
    {
        if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            return false;

        var ok = false;
        //
        // Our valid usernames and passwords
        //
        switch (username)
        {
            case "your user 1 here":
                {
                    if (password == "your password here") ok = true;
                }
                break;
            case "your user 2 here or from DB, whatever":
                {
                    if (password == "pwd") ok = true;
                }
                break;
        }

        return ok;
    }
} // cl

//
// See: http://weblog.west-wind.com/posts/2013/Apr/18/A-WebAPI-Basic-Authentication-Authorization-Filter
// And Asp.Net WebApi Security book.
public class BasicAuthenticationIdentity : GenericIdentity
{
    public BasicAuthenticationIdentity(string name, string password)
        : base(name, "Basic")
    {
        this.Password = password;
    }

    /// <summary>
    /// Basic Auth Password for custom authentication
    /// </summary>
    public string Password { get; set; }
}