【问题标题】:Identity Server Response type not supported: code+id_token不支持身份服务器响应类型:code+id_token
【发布时间】:2019-02-16 14:31:10
【问题描述】:

我有一个身份服务器 4 项目作为服务器,asp.net mvc 5 作为客户端。除了一个偶尔出现的问题外,一切正常:不支持响应类型:code+id_token

我在生产日志查的时候,发现授权url偶尔被编码了两次,然后找不到根本原因并重现。我猜这是 Microsoft.Owin.Security.OpenIdConnect 的内在行为,

未登录用户访问受保护资源时,自动生成授权url。

从我的mvc客户端,“code id_token”首先被编码为“code+id_token”,然后“code+id_token”被编码为“code%2Bid_token”。

在我的身份服务器端,“code%2Bid_token”被解码为“code+id_token”,从而发生验证错误。

以下是我的日志:

INFO  11:54:35 Request starting HTTP/1.1 GET http://login.example.com/connect/authorize?client_id=gjcf_mvc&nonce=636709820590066816.ZWNlNzJmOGQtMGFhNC00NzVkLTllNzktNmE5NTIzN2EzNDE3NThhMmI2OGYtODI5Mi00OTE2LTgzN2MtNGFkZWUzODQ4Nzlk&redirect_uri=https%3A%2F%2Fwww.example.com%2Fsignin-oidc&response_mode=form_post&response_type=code%2Bid_token&scope=openid%2Bprofile%2Bapi1%2BGjcfApi%2Boffline_access&state=OpenIdConnect.AuthenticationProperties 

INFO  11:54:35 Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize

ERROR 11:54:35 Response type not supported: code+id_token
{
  "ClientId": "gjcf_mvc",
  "ClientName": "gjcf_mvc_name",
  "RedirectUri": "https://www.example.com/signin-oidc",
  "AllowedRedirectUris": [
    "https://www.example.com/signin-oidc"
  ],
  "SubjectId": "anonymous",
  "RequestedScopes": "",
  "State": "OpenIdConnect.AuthenticationProperties=5USuW-uf3wCad1ap9VCDDCE6bTKr1mUMZob-yI_vBUsAFqx_7oLv-0f3rTApD5_6NjVf3siQsJKg9cH4T7YA6ra2B_6_Yooq_S0rJW2L3I4a13Gg5DpcESjg8gb4MQSysOm_xLjgXa96gpGN0tTwNmnb6dB6S3c3ttIDPt_JWCI0qHclfprE_RlO4RlY3LqsI3YhGznHUXM9UW-x38KB9vUtdfulXYrWRko35cQmezI3QAIXqOCt_d7qLgL5WBeNRRk8I0QrbfrmhTwwtS1fTBi5vUPujBPi9L14mCeKPbNZIm5w4oqZOznjBhw0k5v2",
  "Raw": {
    "client_id": "gjcf_mvc",
    "nonce": "636709820590066816.ZWNlNzJmOGQtMGFhNC00NzVkLTllNzktNmE5NTIzN2EzNDE3NThhMmI2OGYtODI5Mi00OTE2LTgzN2MtNGFkZWUzODQ4Nzlk",
    "redirect_uri": "https://www.example.com/signin-oidc",
    "response_mode": "form_post",
    "response_type": "code+id_token",
    "scope": "openid+profile+api1+GjcfApi+offline_access",
    "state": "OpenIdConnect.AuthenticationProperties"
  }
}

以下代码在asp.net mvc启动中:

app.UseCookieAuthentication(new CookieAuthenticationOptions
                {
                    AuthenticationType = "Cookies",
                    Provider = new CookieAuthenticationProvider
                    {
                        OnResponseSignIn = context =>
                        {
                            context.Properties.AllowRefresh = true;
                            context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(40);
                        }
                    }
                });

                GjcfOpenIdConnectConfiguration conf = GjcfOpenIdConnectConfiguration.Instance;

                app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
                {
                    ClientId = conf.ClientId,
                    ClientSecret = conf.ClientSecret,
                    Authority = conf.Authority,
                    RedirectUri = conf.RedirectUri,
                    PostLogoutRedirectUri = conf.PostLogoutRedirectUri,

                    ResponseType = conf.ResponseType,
                    Scope = conf.Scope,
                    SignInAsAuthenticationType = "Cookies",


                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthorizationCodeReceived = async n =>
                        {
                            // use the code to get the access and refresh token
                            var tokenClient = new TokenClient(
                                conf.TokenAddress,
                                conf.ClientId,
                                conf.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(
                                new Uri(conf.UserInfoAddress),
                                tokenResponse.AccessToken);

                            var userInfoResponse = await userInfoClient.GetAsync();

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

                            id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
                            id.AddClaim(new Claim("expires_at",
                                DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
                            id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
                            id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
                            id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
                            id.AddClaim(new Claim(ClaimTypes.NameIdentifier,
                                n.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value));
                            id.AddClaim(new Claim(
                                "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider",
                                n.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value));

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

                        AuthenticationFailed = (context) =>
                        {
                            if (context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311"))
                            {
                                context.SkipToNextMiddleware();
                                return Task.FromResult(0);
                            }
                            return Task.FromResult(0);
                        },

                        RedirectToIdentityProvider = n =>
                        {                                    
                            // if signing out, add the id_token_hint
                            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
                            {
                                var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

                                if (idTokenHint != null)
                                {
                                    n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                                }

                            }

                            return Task.FromResult(0);
                        }
                    }
                });

当用户访问标记为Authorize属性的action时,生成authorize url。

[Authorize]
        public ActionResult IdentityServerJump(string returnUrl)
        {
            if (!string.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

【问题讨论】:

  • 能否提供一些相关代码。
  • 欢迎来到 SO,您在提出第一个问题方面做得很好。正如@Dimitar 提到的,代码将有助于识别您遇到的双重编码。
  • @Michael Zhang 你找到解决方案了吗?我有同样的问题。我认为这取决于用户使用的浏览器。但我不确定如何解决它。

标签: c# .net owin identityserver4 openid-connect


【解决方案1】:

虽然您没有提供代码,但以下部分存在问题

在我的 mvc 客户端,“code id_token”首先编码为“code+id_token”,然后“code+id_token”编码为“code%2Bid_token”.

不知道为什么要进行双重编码。从 IS4(身份服务器)端,它会在不知道查询段是双重编码的情况下进行 URL 解码。

对此的更正是使用单一编码。根据 OpenID Connect 规范,坚持使用 %20 对空格进行编码,不要像现在这样进行双重编码。!!。我也欢迎您在this answer 阅读有关 +%20 的信息

【讨论】:

  • 谢谢,我对编码什么都没做,url是由oidc中间件生成和编码的,我每天在日志中只看到一两个错误
  • @MichaelZhang 一个示例代码真的很有帮助:)
猜你喜欢
  • 2020-07-26
  • 2021-06-16
  • 2022-07-20
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多