TLDR; In the context of using IdentityServer4
I have worked through the IdentityServer quickstarts and have a working MVC client talking to a IdentityServer instance (apologies if using the wrong terminology). I am using External Authentication (Google) and do not have anything mildly complicated such as local logins / database etc. I am not using ASP.NET Identity. This is all working just fine.
I can successfully authenticate in my MVC app and the following code produces the claims in the screenshot below:
@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>
My setup is as follows - you can see the output of this as above:
In 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;
});
Client definition
// 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 definition
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"}
}
};
The answer is amazingly unobvious: the sample code provided by IdentityServer4 works as long as you have the following configuration (in the Identity Server Startup.cs):
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients()
.AddTestUsers(Config.GetUsers()); //<--- this line here
Why? Because AddTestUsers is doing a bunch of the plumbing you need to do in your own world. The walkthrough implicitly assume you move to EF or ASP.NET Identity etc and make it unclear what you have to do if you aren't going to use these data stores. In short, you need to:
My IdentityServer Startup.cs ended up looking like this (I want to do in memory deliberately, but obviously not use the test users provided in the samples):
services.AddSingleton(new InMemoryUserStore()); //<-- new
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddProfileService<UserProfileService>(); //<-- new
Turns out Google does return email as part of the claim. The scopes in the code sample of the question worked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With