如何使 ASP.NET 使用 Owin OpenId Connect 库创建经过身份验证的会话?

How to make ASP.NET create authenticated session with Owin OpenId Connect library?

在网上搜索了大量示例后,我正在努力解决看似相当简单的要求。

我正在尝试扩展目前使用表单身份验证的现有 ASP.NET 应用程序,以便它可以使用 OpenID Connect 进行身份验证以及来自身份提供者的一些角色信息。特别是我正在与我无法控制的现有托管身份提供者集成。

我正在使用 ASP.NET MVC 和 OpenIdConnect 的 Owin 组件。即,

Microsoft.Owin.Security
Microsoft.Owin.Security.Cookies
Microsoft.Owin.Security.OpenIdConnect

我成功地能够:

  1. 在 Web 浏览器中 -- 导航到使用 [Authorize] 属性保护的控制器方法
  2. Owin 组件正确地将我重定向到我可以进行身份​​验证的身份提供者,然后我被重定向回我的应用程序(注意:我的身份提供者要求传入 redirect_uri,所以我我目前正在将其设置为 OpenIdConnectAuthenticationOptions 启动配置的一部分。)
  3. 当重定向回我的应用程序时,我能够看到 access_token 和 id_token 作为查询字符串的一部分。此外,我已经能够使用 access_token 调用用户信息端点并正确地导出有关使用该令牌的用户的信息。

到目前为止一切顺利!然而。

我未能掌握的内容以及我见过的大多数 Owin 示例似乎都没有解释:需要什么(如果有的话)额外配置才能 ASP.NET 实际创建经过身份验证的会话在我的应用程序中基于从身份提供者重定向回我的应用程序。

我从文档中得到的总体感觉是我不必在 Owin 库中进行额外的配置——一旦我将系统配置为使用 cookie 身份验证和 OpenId Connect 库——它应该只是工作。然而,这似乎并不像看起来那么容易。我猜我错过了什么。

一些具体的considerations/observations:

  1. 我发现的许多示例不需要在 OpenIdConnectAuthenticationOptions 中设置 RedirectUri,但我的身份提供者每次都需要设置此参数。
  2. 我发现的很少的示例可以解释因命中 RedirectUri 而触发的控制器方法是否应使用 [Authorize] 进行保护或保持匿名。在我的测试中,如果我将其标记为 [Authorize] 我会进入无限重定向循环。如果我保持匿名,我可以在请求信息中看到令牌,但永远不会创建 ASP.NET 会话。例如,Request.IsAuthenticated 始终为假。
  3. 作为测试,我在几个 OpenIdConnectAuthenticationNotifications() 事件中设置了断点,目前我只看到我的代码中断到 RedirectToIdentityProvider 事件,而其他 NONE 似乎命中了 --这让我相信我没有配置这个权利。
  4. 根据我发现的建议,我在 web.config 中以这种方式设置了身份验证节点,但如果我排除该节点,它似乎没有什么不同。

    <system.web>
        <authentication mode="None" />
    </system.web>
    

总结一下:

  1. 我是否需要专门编写代码来处理从身份提供者返回的重定向,以便为给定用户手动设置 ASP.NET 会话(cookie 等)?和
  2. 如果是这样,这段代码应该进入由于 RedirectUri 被命中而调用的控制器方法,还是应该进入 OpenIdConnectAuthenticationNotifications() 中可用的 "Notifications" 事件之一?

最后,如果我不应该在从身份提供者重定向后手动设置经过身份验证的会话(如果它应该自动工作),对于此配置的常见错误有什么建议吗?

完整性:

我的Owin管道启动配置方法:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseOpenIdConnectAuthentication(
        new OpenIdConnectAuthenticationOptions
        {
            //no problems on these as far as I can tell
            ClientId = "client_id_string",
            ClientSecret = "client_secret_string",
            Authority = "url_to_identity_provider",
            Scope = "email name etc",

            //I'm properly redirected to this URL but not sure 
            //if I should need to create the session manually
            RedirectUri = "http://mymachine/mymvcapp/authorize",

            //this causes the redirection to come with the access_token, 
            //which is valid
            ResponseType = "token",
            SignInAsAuthenticationType = "Cookies",
            Notifications = new OpenIdConnectAuthenticationNotifications()
            {
                RedirectToIdentityProvider = (context) =>
                {
                    //I'm able to break into this method
                    return Task.FromResult(0);
                },
                MessageReceived = (context) =>
                {
                    //doesn't seem to run this line
                    return Task.FromResult(0);
                },
                SecurityTokenReceived = (context) =>
                {
                    //doesn't seem to run this line
                    return Task.FromResult(0);
                },
                SecurityTokenValidated = (context) =>
                {
                    //doesn't seem to run this line
                    return Task.FromResult(0);
                },
                AuthorizationCodeReceived = (context) =>
                {
                    //doesn't seem to run this line
                    return Task.FromResult(0);
                },
                AuthenticationFailed = (context) =>
                {
                    //doesn't seem to run this line
                    return Task.FromResult(0);
                },
            },
        });
}

正确启动登录流程的安全方法:

[Authorize]
public class HomeController : Controller
{
    //I'm sent to the login flow the first time this is hit
    public ActionResult Index()
    {
        return View();
    }
}

我在 RedirectUri 的方法确实被调用但确实表明 ASP.NET 已验证会话已创建:

public class AuthorizeController : Controller
{
    // [Authorize] -- currently this Authorize attribute is turned off 
    //so the method is anonymous. 

    //If I turn that back on, I get infininte redirect loops to 
    //the Identity Provider
    public ActionResult Index()
    {
        //the incoming request to this controller method from the 
        //identity provider DOES include valid access_token and id_token 
        //(which can be used against the user info endpoint) but does not 
        //create a valid ASP.NET session for my web app

        //Request.IsAuthenticated is always false

        //should there be a manual creation of the ASP.NET 
        //session/cookie information in this controller method?

        //note: to me it would make most sense if this attribute was not
        //anonymous since it's unlikely that the Request would ever appear
        //as IsAuthenticated == true, but if you read the entire question
        //it will be clear why I'm trying this method with anonymous access

        return View();
    }
}

如您所知,您不能在外部服务器用来通知您用户已授权的方法上添加 [Authorize] 属性 - 会话尚未授权,您只是收到通知应该

幸运的是,创建该会话并不困难: How can I manually create a authentication cookie instead of the default method?

(我很确定你必须自己用基本的 Microsoft Owin 东西来做 - 如果你愿意,你总是 可以 自己做。)