IdentityServer4 - 缺少来自 Google 的声明
IdentityServer4 - missing claims from Google
TLDR; 在使用 IdentityServer4 的上下文中
- 如何从 Google 获取电子邮件地址和高清声明?
- 如何让 User.Identity.Name 被填充?
我已经完成了 IdentityServer quickstarts 并且有一个工作的 MVC 客户端与 IdentityServer 实例对话(如果使用错误的术语,我们深表歉意)。我正在使用外部身份验证 (Google),并且没有任何稍微复杂的东西,例如本地登录/数据库等。我 没有 使用 ASP.NET 身份。这一切都很好。
我可以在我的 MVC 应用程序中成功进行身份验证,并且以下代码会在下面的屏幕截图中生成声明:
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
<dt>Identity.Name</dt>
<dd> @User.Identity.Name</dd>
<dt>IsAuthenticated</dt>
<dd>@User.Identity.IsAuthenticated</dd>
问题:
- 我无法从 Google 中检索到额外声明(正确的术语?)。特别是 'hd' 甚至 'email' - 请注意,它们不会出现在上面屏幕截图的声明中。如何从 Google 获取电子邮件地址和高清声明?我错过了什么或做错了什么?
- 注意User.Identity.Name的输出是空的。为什么会这样,我如何填充它?这似乎是 User.Identity 中唯一未设置的 属性。
我的设置如下 - 您可以看到上面的输出:
客户端(MVC)
在Startup.cs中,ConfigureServices
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = Configuration["App:Urls:IdentityServer"];
options.RequireHttpsMetadata = false;
options.Resource = "openid profile email";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("domain");
options.ClientId = "ctda-web";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
});
身份服务器
客户端定义
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "ctda-web",
ClientName = "Company To Do Web App",
AllowedGrantTypes = GrantTypes.Implicit,
EnableLocalLogin = false,
// where to redirect to after login
RedirectUris = { "http://localhost:53996/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:53996/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"domain"
}
}
IdentityResource 定义
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
{
Name = "domain",
DisplayName = "Google Organisation",
Description = "The hosted G Suite domain of the user, if part of one",
UserClaims = new List<string> { "hd"}
}
};
我可以告诉你如何获取要返回的电子邮件。
有两种方法可以做到这一点,但它们都需要您将电子邮件范围添加到初始请求中。只发送 openId 是行不通的。
Openid email
用户信息请求
现在,当您取回访问令牌后,您可以
https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token={access token}
回应
{
"family_name": "Lawton",
"name": "Linda Lawton",
"picture": "https://lh5.googleusercontent.com/-a1CWlFnA5xE/AAAAAAAAAAI/AAAAAAAAl1I/UcwPajZOuN4/photo.jpg",
"gender": "female",
"email": "xxxx@gmail.com",
"link": "https://plus.google.com/+LindaLawton",
"given_name": "Linda",
"id": "117200475532672775346",
"verified_email": true
}
令牌信息请求:
使用 id 令牌
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={token id}
回应
{
"azp": "07408718192.apps.googleusercontent.com",
"aud": "07408718192.apps.googleusercontent.com",
"sub": "00475532672775346",
"email": "XX@gmail.com",
"email_verified": "true",
"at_hash": "8ON2HwraMXbPpP0Nwle8Kw",
"iss": "https://accounts.google.com",
"iat": "1509967160",
"exp": "1509970760",
"alg": "RS256",
"kid": "d4ed62ee21d157e8a237b7db3cbd8f7aafab2e"
}
至于如何填充您的控制器,我帮不上什么忙。
答案出奇地不明显:只要您具有以下配置(在 Identity Server Startup.cs 中),IdentityServer4 提供的示例代码就可以工作:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients()
.AddTestUsers(Config.GetUsers()); //<--- this line here
为什么?因为 AddTestUsers 正在做一堆 the plumbing 你需要在你自己的世界里做。该演练隐含地假设您移动到 EF 或 ASP.NET Identity 等,并且不清楚如果您不打算使用这些数据存储,您必须做什么。简而言之,您需要:
- 创建一个对象来表示用户(here's a starter)
- 创建 persistance/query class(here's a starter)
- 创建一个 IProfileService 实例,将这一切联系在一起,放入您对 User 和 UserStore 的定义 (here's a starter)
- 添加适当的绑定等
我的 IdentityServer Startup.cs 最终看起来像这样(我想故意在内存中做,但显然不使用示例中提供的测试用户):
services.AddSingleton(new InMemoryUserStore()); //<-- new
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddProfileService<UserProfileService>(); //<-- new
原来 Google 确实 return 电子邮件作为索赔的一部分。问题的代码示例中的范围有效。
TLDR; 在使用 IdentityServer4 的上下文中
- 如何从 Google 获取电子邮件地址和高清声明?
- 如何让 User.Identity.Name 被填充?
我已经完成了 IdentityServer quickstarts 并且有一个工作的 MVC 客户端与 IdentityServer 实例对话(如果使用错误的术语,我们深表歉意)。我正在使用外部身份验证 (Google),并且没有任何稍微复杂的东西,例如本地登录/数据库等。我 没有 使用 ASP.NET 身份。这一切都很好。
我可以在我的 MVC 应用程序中成功进行身份验证,并且以下代码会在下面的屏幕截图中生成声明:
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
<dt>Identity.Name</dt>
<dd> @User.Identity.Name</dd>
<dt>IsAuthenticated</dt>
<dd>@User.Identity.IsAuthenticated</dd>
问题:
- 我无法从 Google 中检索到额外声明(正确的术语?)。特别是 'hd' 甚至 'email' - 请注意,它们不会出现在上面屏幕截图的声明中。如何从 Google 获取电子邮件地址和高清声明?我错过了什么或做错了什么?
- 注意User.Identity.Name的输出是空的。为什么会这样,我如何填充它?这似乎是 User.Identity 中唯一未设置的 属性。
我的设置如下 - 您可以看到上面的输出:
客户端(MVC)
在Startup.cs中,ConfigureServices
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = Configuration["App:Urls:IdentityServer"];
options.RequireHttpsMetadata = false;
options.Resource = "openid profile email";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("domain");
options.ClientId = "ctda-web";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
});
身份服务器
客户端定义
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "ctda-web",
ClientName = "Company To Do Web App",
AllowedGrantTypes = GrantTypes.Implicit,
EnableLocalLogin = false,
// where to redirect to after login
RedirectUris = { "http://localhost:53996/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:53996/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"domain"
}
}
IdentityResource 定义
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
{
Name = "domain",
DisplayName = "Google Organisation",
Description = "The hosted G Suite domain of the user, if part of one",
UserClaims = new List<string> { "hd"}
}
};
我可以告诉你如何获取要返回的电子邮件。
有两种方法可以做到这一点,但它们都需要您将电子邮件范围添加到初始请求中。只发送 openId 是行不通的。
Openid email
用户信息请求
现在,当您取回访问令牌后,您可以
https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token={access token}
回应
{
"family_name": "Lawton",
"name": "Linda Lawton",
"picture": "https://lh5.googleusercontent.com/-a1CWlFnA5xE/AAAAAAAAAAI/AAAAAAAAl1I/UcwPajZOuN4/photo.jpg",
"gender": "female",
"email": "xxxx@gmail.com",
"link": "https://plus.google.com/+LindaLawton",
"given_name": "Linda",
"id": "117200475532672775346",
"verified_email": true
}
令牌信息请求:
使用 id 令牌
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={token id}
回应
{
"azp": "07408718192.apps.googleusercontent.com",
"aud": "07408718192.apps.googleusercontent.com",
"sub": "00475532672775346",
"email": "XX@gmail.com",
"email_verified": "true",
"at_hash": "8ON2HwraMXbPpP0Nwle8Kw",
"iss": "https://accounts.google.com",
"iat": "1509967160",
"exp": "1509970760",
"alg": "RS256",
"kid": "d4ed62ee21d157e8a237b7db3cbd8f7aafab2e"
}
至于如何填充您的控制器,我帮不上什么忙。
答案出奇地不明显:只要您具有以下配置(在 Identity Server Startup.cs 中),IdentityServer4 提供的示例代码就可以工作:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients()
.AddTestUsers(Config.GetUsers()); //<--- this line here
为什么?因为 AddTestUsers 正在做一堆 the plumbing 你需要在你自己的世界里做。该演练隐含地假设您移动到 EF 或 ASP.NET Identity 等,并且不清楚如果您不打算使用这些数据存储,您必须做什么。简而言之,您需要:
- 创建一个对象来表示用户(here's a starter)
- 创建 persistance/query class(here's a starter)
- 创建一个 IProfileService 实例,将这一切联系在一起,放入您对 User 和 UserStore 的定义 (here's a starter)
- 添加适当的绑定等
我的 IdentityServer Startup.cs 最终看起来像这样(我想故意在内存中做,但显然不使用示例中提供的测试用户):
services.AddSingleton(new InMemoryUserStore()); //<-- new
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddProfileService<UserProfileService>(); //<-- new
原来 Google 确实 return 电子邮件作为索赔的一部分。问题的代码示例中的范围有效。