У меня есть проект сервера удостоверений 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");
}
}