Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在使用OpenID Connect和ASP.NET的第1方应用程序之间实现SSO?_C#_Asp.net_Cookies_Openid Connect - Fatal编程技术网

C# 如何在使用OpenID Connect和ASP.NET的第1方应用程序之间实现SSO?

C# 如何在使用OpenID Connect和ASP.NET的第1方应用程序之间实现SSO?,c#,asp.net,cookies,openid-connect,C#,Asp.net,Cookies,Openid Connect,我有一种似乎很奇怪的情况,我不完全确定我是不是走对了路,但接下来 我有一个使用Identity Server实现的OpenId连接提供程序。此OIDC提供程序保护一个API、多个第一方应用程序和多个第三方应用程序。第三方应用程序运行良好。正是第一方应用程序让我很为难,因为我试图在所有这些应用程序中执行SSO,即,如果您登录/退出其中一个应用程序,那么您也会登录/退出其他应用程序。这与您单独登录/注销的第三方应用不同,但它们依赖于您现有的OIDC会话 我的第一方应用程序能够识别来自OIDC提供商的

我有一种似乎很奇怪的情况,我不完全确定我是不是走对了路,但接下来

我有一个使用Identity Server实现的OpenId连接提供程序。此OIDC提供程序保护一个API、多个第一方应用程序和多个第三方应用程序。第三方应用程序运行良好。正是第一方应用程序让我很为难,因为我试图在所有这些应用程序中执行SSO,即,如果您登录/退出其中一个应用程序,那么您也会登录/退出其他应用程序。这与您单独登录/注销的第三方应用不同,但它们依赖于您现有的OIDC会话

我的第一方应用程序能够识别来自OIDC提供商的身份验证cookie,因为它们共享相同的数据保护密钥。如果我直接登录到OIDC提供程序,然后导航到第1方应用程序,该应用程序会检测到cookie并指示我已登录

不起作用的是,如果我执行我刚才描述的登录流(直接登录到OIDC提供商,然后导航到第1方应用程序),它实际上从未经过整个OIDC流,因此产生的索赔缺少访问令牌和刷新令牌声明,OIDC通常在命中令牌端点后注入

所以我的问题是:有没有更好的方法来完成我要做的事情,或者有没有一种方法可以强制客户机完成OIDC流,即使它已经有了auth cookie

Startup.cs来自第1方应用程序

var dataProtector = //Custom DataProtector

app.SetDefaultSignInAsAuthenticationType("oidc");

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = "Cookies",
    CookieName = "AuthTicket",
    CookieDomain = Properties.Settings.Default.CookieDomain,
    CookiePath = "/",
    ExpireTimeSpan = TimeSpan.FromDays(20),
    LoginPath = new PathString("/Account/Login"),
    LogoutPath = new PathString("/Account/Logout"),
    TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector))
});

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    AuthenticationType = "oidc",
    ClientId = Properties.Settings.Default.ClientId,
    ClientSecret = Properties.Settings.Default.ClientSecret,
    Authority = Properties.Settings.Default.Authority,
    RedirectUri = $"{Properties.Settings.Default.BaseSiteUrl}/account/login",
    ResponseType = "code id_token",
    Scope = "openid profile api",
    TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name",
        RoleClaimType = "role"
    },
    SignInAsAuthenticationType = "Cookies",
    Notifications = new OpenIdConnectAuthenticationNotifications
    { 
        AuthorizationCodeReceived = async n =>
        {
            var tokenClient = new TokenClient($"{Properties.Settings.Default.Authority}/connect/token",
                    Properties.Settings.Default.ClientId,
                    Properties.Settings.Default.ClientSecret);

            var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);

            if (tokenResponse.IsError)
            {
                throw new Exception(tokenResponse.Error);
            }

            // use the access token to retrieve claims from userinfo
            var userInfoClient = new UserInfoClient($"{Properties.Settings.Default.Authority}/connect/userinfo");

            var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);

            // create new identity
            var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
            id.AddClaims(userInfoResponse.Claims);

            if (!string.IsNullOrEmpty(tokenResponse.AccessToken))
            {
                id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
                id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
            }

            if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
            {
                id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
            }

            if (!string.IsNullOrEmpty(n.ProtocolMessage.IdToken))
            {
                id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
            }

            if (n.AuthenticationTicket.Identity.Claims.Any(c => c.Type == "sid"))
            {
                id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
            }

            n.AuthenticationTicket = new AuthenticationTicket(new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
                n.AuthenticationTicket.Properties);
        }
    }
});

我并没有完全理解这个场景,但据我所知,您希望更改身份提供程序获得的SSO行为

协议为您提供了控制这一点的能力。身份验证请求可以包含
提示符
参数,以选择与应用程序的所需交互行为

提示

可选。ASCII字符串值的空格分隔、区分大小写的列表 指定授权服务器是否提示最终用户 用于重新验证和同意

有四个参数可供选择(摘自规范章节,无需说明)

  • 没有
  • 登录
  • 同意
  • 选择您的帐户
据我所知,您可以使用prompt=login强制用户登录,或prompt=select\u account在不同用户之间更改用户帐户。您还可以将其与login\u hint参数结合使用,以微调此参数的使用


此外,如果您想在top pf prompt参数上强制执行SSO行为,可以使用prompt=none

我没有完全理解该场景,但据我所知,您希望更改通过身份提供者获得的SSO行为

协议为您提供了控制这一点的能力。身份验证请求可以包含
提示符
参数,以选择与应用程序的所需交互行为

提示

可选。ASCII字符串值的空格分隔、区分大小写的列表 指定授权服务器是否提示最终用户 用于重新验证和同意

有四个参数可供选择(摘自规范章节,无需说明)

  • 没有
  • 登录
  • 同意
  • 选择您的帐户
据我所知,您可以使用prompt=login强制用户登录,或prompt=select\u account在不同用户之间更改用户帐户。您还可以将其与login\u hint参数结合使用,以微调此参数的使用

此外,如果您想强制在top pf prompt参数上执行SSO行为,可以使用prompt=none