【发布时间】: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