当拒绝令牌请求时,自定义OWIN/OAuth的HTTP状态码

15

我已经派生出OAuthAuthorizationServerProvider来验证客户端和资源所有者。

当我验证资源所有者时,发现他们的凭证无效时,我调用context.Rejected()但HTTP响应显示状态码为HTTP/400 Bad Request而不是我期望的HTTP/401 Unauthorized

如何自定义OAuthAuthorizationServerProvider的响应HTTP状态码?

2个回答

16

这是我们如何覆盖 OwinMiddleware...首先我们在 Owin 之上创建了自己的中间件...我想我们遇到了类似于你所遇到的问题。

首先需要创建一个常量:

public class Constants
{
    public const string OwinChallengeFlag = "X-Challenge";
}

然后我们覆盖了OwinMiddleware

public class AuthenticationMiddleware : OwinMiddleware
{
    public AuthenticationMiddleware(OwinMiddleware next) : base(next) { }

    public override async Task Invoke(IOwinContext context)
    {
        await Next.Invoke(context);

        if (context.Response.StatusCode == 400 && context.Response.Headers.ContainsKey(Constants.OwinChallengeFlag))
        {
            var headerValues = context.Response.Headers.GetValues(Constants.OwinChallengeFlag);
            context.Response.StatusCode = Convert.ToInt16(headerValues.FirstOrDefault());
            context.Response.Headers.Remove(Constants.OwinChallengeFlag);
        }

    }
}
在startup.Auth文件中,我们允许覆盖调用Owin命令。
public void ConfigureAuth(IAppBuilder app)
    ....
        app.Use<AuthenticationMiddleware>(); //Allows override of Invoke OWIN commands
    ....

    }

在ApplicationOAuthProvider中,我们修改了GrantResourceOwnerCredentials方法。

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        using (UserManager<IdentityUser> userManager = _userManagerFactory())
        {
            IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                context.Response.Headers.Add(Constants.OwinChallengeFlag, new[] { ((int)HttpStatusCode.Unauthorized).ToString() }); //Little trick to get this to throw 401, refer to AuthenticationMiddleware for more
                //return;
            }
            ....

明天我会尝试这个,如果成功了我会确认给你的!提前感谢你 ;) - Matías Fidemraizer
1
是的,绝对没问题。认证已经在工作了。我只想在拒绝认证时将400更改为401。 - Matías Fidemraizer
是的,我们的身份验证已经工作了,但我们也得到了错误的状态。我们使用了创建新项目时附带的身份验证代码,其中包含身份验证。如果您的操作与此不同,我不确定为什么没有被调用。您在哪里验证登录?并设置声明部分?如果您确实使用声明的话。 - Greg P
1
我成功让中间件工作了(这是关于在应用程序中注册中间件的顺序问题...)。顺便说一下,虽然设置了401代码,但响应仍然是HTTP/400。 - Matías Fidemraizer
1
当在 app.UseOAuthAuthorizationServer 之前编写 app.Use<AuthenticationMiddleware>() 时,答案有效。 - Rajat
显示剩余4条评论

0

我采用了稍微不同的方法,使用System.Security.Authentication.AuthenticationException和异常中间件。

身份验证服务器提供程序:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        using (UserManager<IdentityUser> userManager = _userManagerFactory())
        {
            IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                throw new AuthenticationException("The user name or password is incorrect.");
            }
            ....

中间件:

public class ExceptionMiddleware : OwinMiddleware
    {
        public ExceptionMiddleware(OwinMiddleware next) : base(next)
        {
        }

        public override async Task Invoke(IOwinContext context)
        {
            try
            {
                await Next.Invoke(context);
            }
            catch (Exception ex)
            {
                HandleException(ex, context);
            }
        }

        private void HandleException(Exception ex, IOwinContext owinContext)
        {
            var errorDetails = new ErrorDetails()
            {
                Detail = ex.Message,
                Status = (int)HttpStatusCode.InternalServerError
            };

            switch (ex)
            {
                case AuthenticationException _:
                    errorDetails.Status = (int)HttpStatusCode.Unauthorized;
                    errorDetails.Title = "invalid_grant";
                    break;
                case [..]
                case Exception _:
                    errorDetails.Title = "An unexpected error occured";
                    break;
            }

            var serializedError = errorDetails.ToString();
            Log.Error($"Returning error response: {serializedError}");
            owinContext.Response.StatusCode = errorDetails.Status;
            owinContext.Response.ContentType = "application/json";
            owinContext.Response.Write(serializedError);
        }

        private class ErrorDetails
        {
            public int Status { get; set; }
            public string Title { get; set; }
            public string Detail { get; set; }

            public override string ToString()
            {
                return JsonSerializer.Serialize(this);
            }
        }

应用配置:

public void Configure(IAppBuilder app)
    ....
        app.Use<ExceptionMiddleware>();
        // Configure auth here
    ....

    }

结果:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接