【问题标题】:OWIN OpenID connect authorization fails to authorize secured controller / actionsOWIN OpenID 连接授权无法授权安全控制器/操作
【发布时间】:2016-03-07 15:38:24
【问题描述】:

我正在开展一个项目,其中第三方提供商将充当基于 Oauth2 的授权服务器。一个基于 Asp.net MVC 5 的客户端,它将用户发送到授权服务器进行身份验证(使用登录名/密码),并且身份验证服务器将返回一个访问令牌返回给 MVC 客户端。对资源服务器 (API) 的任何进一步调用都将使用访问令牌进行。

为了实现这一点,我使用了 Microsoft.Owin.Security.OpenIdConnect 和 UseOpenIdConnectAuthentication 扩展。我能够成功重定向并从身份验证服务器获取访问令牌,但客户端没有创建身份验证 Cookie。每次我尝试访问安全页面时,都会获得带有访问令牌的回调页面。

我在这里缺少什么?我当前的代码如下。

安全控制器操作:

namespace MvcWebApp.Controllers
{    
    public class SecuredController : Controller
    {
        // GET: Secured
        [Authorize]
        public ActionResult Index()
        {
            return View((User as ClaimsPrincipal).Claims);
        }
    }
}

启动类:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType("ClientCookie");

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            AuthenticationType = "ClientCookie",
            CookieName = CookieAuthenticationDefaults.CookiePrefix + "ClientCookie",
            ExpireTimeSpan = TimeSpan.FromMinutes(5)
        });

        // ***************************************************************************
        // Approach 1 : ResponseType = "id_token token"
        // ***************************************************************************
        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
            SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
            Authority = "https://thirdparty.com.au/oauth2",
            ClientId = "_Th4GVMa0JSrJ8RKcZrzbcexk5ca",
            ClientSecret = "a3GVJJbLHkrn9nJRj3IGNvk5eGQa",
            RedirectUri = "http://mvcwebapp.local/",
            ResponseType = "id_token token",
            Scope = "openid",

            Configuration = new OpenIdConnectConfiguration
            {
                AuthorizationEndpoint = "https://thirdparty.com.au/oauth2/authorize",
                TokenEndpoint = "https://thirdparty.com.au/oauth2/token",
                UserInfoEndpoint = "https://thirdparty.com.au/oauth2/userinfo",
            },

            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                SecurityTokenValidated = n =>
                {
                    var token = n.ProtocolMessage.AccessToken;

                    // persist access token in cookie
                    if (!string.IsNullOrEmpty(token))
                    {
                        n.AuthenticationTicket.Identity.AddClaim(
                            new Claim("access_token", token));
                    }

                    return Task.FromResult(0);
                },

                AuthenticationFailed = notification =>
                {
                    if (string.Equals(notification.ProtocolMessage.Error, "access_denied", StringComparison.Ordinal))
                    {
                        notification.HandleResponse();

                        notification.Response.Redirect("/");
                    }

                    return Task.FromResult<object>(null);
                }
            }
        });

        // ***************************************************************************
        // Approach 2 : ResponseType = "code"
        // ***************************************************************************
        //app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        //{
        //    AuthenticationMode = AuthenticationMode.Active,
        //    AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
        //    SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
        //    Authority = "https://thirdparty.com.au/oauth2",
        //    ClientId = "_Th4GVMa0JSrJ8RKcZrzbcexk5ca",
        //    ClientSecret = "a3GVJJbLHkrn9nJRj3IGNvk5eGQa",
        //    RedirectUri = "http://mvcwebapp.local/",
        //    ResponseType = "code",
        //    Scope = "openid",

        //    Configuration = new OpenIdConnectConfiguration
        //    {
        //        AuthorizationEndpoint = "https://thirdparty.com.au/oauth2/authorize",
        //        TokenEndpoint = "https://thirdparty.com.au/oauth2/token",
        //        UserInfoEndpoint = "https://thirdparty.com.au/oauth2/userinfo",
        //    },

        //    Notifications = new OpenIdConnectAuthenticationNotifications
        //    {
        //        AuthorizationCodeReceived = async (notification) =>
        //        {
        //            using (var client = new HttpClient())
        //            {
        //                var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);                                        
        //                var request = new HttpRequestMessage(HttpMethod.Get, configuration.TokenEndpoint);
        //                request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
        //                {
        //                    {OpenIdConnectParameterNames.ClientId, notification.Options.ClientId},
        //                    {OpenIdConnectParameterNames.ClientSecret, notification.Options.ClientSecret},
        //                    {OpenIdConnectParameterNames.Code, notification.ProtocolMessage.Code},
        //                    {OpenIdConnectParameterNames.GrantType, "authorization_code"},
        //                    {OpenIdConnectParameterNames.ResponseType, "token"},
        //                    {OpenIdConnectParameterNames.RedirectUri, notification.Options.RedirectUri}
        //                });

        //                var response = await client.SendAsync(request, notification.Request.CallCancelled);
        //                response.EnsureSuccessStatusCode();

        //                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

        //                // Add the access token to the returned ClaimsIdentity to make it easier to retrieve.
        //                notification.AuthenticationTicket.Identity.AddClaim(new Claim(
        //                    type: OpenIdConnectParameterNames.AccessToken,
        //                    value: payload.Value<string>(OpenIdConnectParameterNames.AccessToken)));
        //            }
        //        }
        //    }

        //});

    }
}             

【问题讨论】:

    标签: c# oauth-2.0 wso2 owin openid-connect


    【解决方案1】:

    TL;DR:使用ResponseType = "id_token token",它应该可以工作。

    在 OpenID Connect 中,response_type=token 不被视为合法值:http://openid.net/specs/openid-connect-core-1_0.html#Authentication

    有时出于向后兼容性的原因,response_type=token 不受 MSFT 开发的 OIDC 中间件的支持:当 OpenID Connect 提供程序没有返回 id_token 时总是抛出异常(这也排除了有效的 code流动)。你可以找到更多关于这个其他SO post的信息。

    (备注:在 SecurityTokenValidated 中,您正在使用 n.AuthenticationTicket = new AuthenticationTicket(...) 替换由 OIDC 中间件创建的票证:这不是推荐的方法,并且会导致 ClaimsIdentity 缺少基本声明。您应该考虑删除分配并像您为 access_token 声明所做的那样简单地添加新声明)

    【讨论】:

    • 我试过 response_type = "id_token token",但看起来我使用的身份服务器(WSO2 5.0.0)确实支持它。我将不得不等到下一个版本出来才能获得完整的 openid 连接功能。当我更改响应 response_type = "code" 时,它完成了身份验证过程的第一部分并返回 URL 中的代码,但下一个获取访问令牌的请求不会发生,并且在 OWIN 管道中没有创建身份。
    • 确实,好像不支持:stackoverflow.com/a/29140396/542757。遗憾的是,您不能使用代码流,因为 OIDC 中间件不支持它(很遗憾,我多次对 ASP.NET 团队说过)。是否可以选择迁移到 ASP.NET 5?
    • 如果 WSO2 还没有准备好,迁移到 ASP.net 5 会有帮助吗? id 服务器将支持多个站点,一些现有的和一些新的(正在构建的)。新的可以使用 ASP.net 5,但旧的使用 4.5。我可能会再等几周,因为 WSO2 很快就会发布下一个版本。我已经用我所做的两种方法更新了这个问题。
    • “如果 WSO2 还没有准备好,迁移到 ASP.net 5 会有帮助吗?”:新的 OIDC 中间件完全支持授权代码流(应该在 WSO2 中可用),所以是的。
    • 我有一个类似的问题,使用 WSO2 5.1.0 stackoverflow.com/questions/34773419 并在 SecurityTokenValidated 中添加了一个断点,但它没有在调试中命中。请指教?
    猜你喜欢
    • 1970-01-01
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2017-05-10
    • 2012-04-07
    相关资源
    最近更新 更多