Тип ответа сервера идентификации не поддерживается: code + id_token - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть проект сервера удостоверений 4 в качестве сервера и asp.net mvc 5 в качестве клиента. все работает нормально, за исключением одной случайной проблемы: тип ответа не поддерживается: code + id_token

Во время моего исследования в производственном журнале я обнаружил, что URL авторизации время от времени кодировался дважды ,, тогда я не могу найти основную причину и воспроизвести. Я предполагаю, что это внутреннее поведение Microsoft.Owin.Security.OpenIdConnect,

Авторизованный URL-адрес генерируется автоматически при посещении защищенного ресурса пользователем, который не входит в систему.

С моей клиентской стороны mvc, «код id_token» сначала кодируется как «code + id_token», затем «code + id_token» кодируется как «код% 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, генерируется URL авторизации.

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

1 Ответ

0 голосов
/ 12 сентября 2018

Несмотря на то, что вы не предоставили код, проблема существует в следующем разделе

Со стороны моего клиента mvc, "code id_token" закодирован как "code + id_token", во-первых тогда "code + id_token" кодируется в "code% 2Bid_token" .

Не уверен, почему вы делаете двойное кодирование.Со стороны IS4 (сервер идентификации) он выполняет декодирование URL, не зная, что сегмент запроса имеет двойную кодировку.

Исправление для этого заключается в использовании одной кодировки.Согласно спецификациям OpenID Connect, придерживайтесь кодировки % 20 для пробелов и не используйте двойное кодирование, как сейчас. !!.Также я приветствую вас прочитать о + против % 20 на этот ответ

...