我将身份服务器4项目作为服务器,并将asp.net mvc 5作为客户端。一切正常,除了一个偶然的问题:不支持响应类型:code + id_token
在通过生产日志进行调查期间,我发现授权URL偶尔被编码两次,因此找不到根本原因并进行重现。我猜这是Microsoft.Owin.Security.OpenIdConnect的内部行为,
未登录的用户访问受保护的资源时,会自动生成授权的URL。
在我的mvc客户端上,首先将“代码id_token”编码为“ code + id_token”,然后将“代码+ 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属性的操作时,将生成授权URL。
[Authorize]
public ActionResult IdentityServerJump(string returnUrl)
{
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
答案 0 :(得分:0)
尽管您未提供代码,但以下部分存在问题
在我的mvc客户端上,“代码id_token”首先被编码为“ code + id_token” ,然后“代码+ id_token”被编码为“ code%2Bid_token” 。
不确定为什么要进行双重编码。从IS4(身份服务器)端,它将在不知道查询段是双重编码的情况下进行URL解码。
对此的更正是使用单个编码。根据OpenID Connect规范,坚持使用 空格的%20 编码,不要像现在这样加倍编码。另外,我也欢迎您在this answer
上阅读 + 与%20