Я использую IdentityServer4 с IdentityServer4.UI с authorization_code
и Google authentication
.
Это мой Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
// ...
const string connectionString = @"Data Source=.\SQLEXPRESS;database=Is4.LearnIs4;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
//.AddProfileService<IdentityServerProfileService>()
//.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
{
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
};
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
});
// Add jwt validation.
services
.AddAuthentication()
.AddGoogle("Google", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "<my-client-id>";
options.ClientSecret = "<my-secret>";
options.AccessType = "offline";
})
.AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "http://localhost:57547";
options.ClientId = "implicit";
options.ResponseType = "id_token";
options.SaveTokens = true;
options.CallbackPath = new PathString("/signin-idsrv");
options.SignedOutCallbackPath = new PathString("/signout-callback-idsrv");
options.RemoteSignOutPath = new PathString("/signout-idsrv");
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
// ...
}
И это мои конфигурации клиента:
/// <summary>
/// Seed database.
/// </summary>
/// <param name="app"></param>
public static void Seed(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in LoadClients())
context.Clients.Add(client.ToEntity());
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in LoadIdentityResources())
context.IdentityResources.Add(resource.ToEntity());
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in LoadApiResources())
context.ApiResources.Add(resource.ToEntity());
context.SaveChanges();
}
}
}
/// <summary>
/// Load pre-defined identity resource.
/// </summary>
/// <returns></returns>
private static IEnumerable<IdentityResource> LoadIdentityResources()
{
var identityResources = new List<IdentityResource>();
identityResources.Add(new IdentityResources.OpenId());
identityResources.Add(new IdentityResources.Profile());
return identityResources;
}
/// <summary>
/// Load pre-defined api resources.
/// </summary>
/// <returns></returns>
private static IEnumerable<ApiResource> LoadApiResources()
{
var apiResources = new List<ApiResource>();
var api1Resource = new ApiResource("api1", "My API");
api1Resource.ApiSecrets = new List<Secret>();
api1Resource.ApiSecrets.Add(new Secret("secret".Sha256()));
apiResources.Add(api1Resource);
return apiResources;
}
/// <summary>
/// Load pre-defined clients.
/// </summary>
/// <returns></returns>
private static IEnumerable<Client> LoadClients()
{
var clients = new List<Client>();
var clientCredentialClient = new Client();
clientCredentialClient.ClientId = "client";
clientCredentialClient.AllowedGrantTypes = GrantTypes.ClientCredentials;
clientCredentialClient.ClientSecrets = new List<Secret>();
clientCredentialClient.ClientSecrets.Add(new Secret("secret".Sha256()));
clientCredentialClient.AllowedScopes = new List<string>();
clientCredentialClient.AllowedScopes.Add("api1");
clients.Add(clientCredentialClient);
var resourceOwnerPasswordClient = new Client();
resourceOwnerPasswordClient.ClientId = "ro.client";
resourceOwnerPasswordClient.AllowedGrantTypes = new List<string>();
resourceOwnerPasswordClient.AllowedGrantTypes.Add(GrantType.ResourceOwnerPassword);
resourceOwnerPasswordClient.AllowedGrantTypes.Add("refresh_token");
resourceOwnerPasswordClient.ClientSecrets = new List<Secret>();
resourceOwnerPasswordClient.ClientSecrets.Add(new Secret("secret".Sha256()));
resourceOwnerPasswordClient.AllowedScopes = new List<string>();
resourceOwnerPasswordClient.AllowedScopes.Add("api1");
resourceOwnerPasswordClient.AllowOfflineAccess = true;
resourceOwnerPasswordClient.RefreshTokenExpiration = TokenExpiration.Sliding;
resourceOwnerPasswordClient.RefreshTokenUsage = TokenUsage.ReUse;
resourceOwnerPasswordClient.SlidingRefreshTokenLifetime = 3600;
clients.Add(resourceOwnerPasswordClient);
var authorizationCodeClient = new Client();
authorizationCodeClient.ClientId = "mvc";
authorizationCodeClient.ClientName = "MVC Client";
authorizationCodeClient.AllowedGrantTypes = new List<string>();
authorizationCodeClient.AllowedGrantTypes.Add(GrantType.AuthorizationCode);
authorizationCodeClient.ClientSecrets = new List<Secret>();
authorizationCodeClient.ClientSecrets.Add(new Secret("secret".Sha256()));
authorizationCodeClient.RedirectUris = new List<string>();
authorizationCodeClient.RedirectUris.Add("http://localhost:4300");
authorizationCodeClient.PostLogoutRedirectUris = new List<string>();
authorizationCodeClient.PostLogoutRedirectUris.Add("http://localhost:5002/signout-callback-oidc");
authorizationCodeClient.RequirePkce = false;
authorizationCodeClient.AllowedScopes = new List<string>();
authorizationCodeClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
authorizationCodeClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
authorizationCodeClient.AllowedScopes.Add("api1");
authorizationCodeClient.AllowOfflineAccess = true;
authorizationCodeClient.RequireConsent = false;
clients.Add(authorizationCodeClient);
var codeClient = new Client();
codeClient.ClientId = "js";
codeClient.ClientName = "JavaScript Client";
codeClient.AllowedGrantTypes = GrantTypes.Code;
codeClient.RequirePkce = true;
codeClient.RequireClientSecret = false;
codeClient.RedirectUris = new List<string>();
codeClient.RedirectUris.Add("http://localhost:5003/callback.html");
codeClient.PostLogoutRedirectUris = new List<string>();
codeClient.PostLogoutRedirectUris.Add("http://localhost:5003/index.html");
codeClient.AllowedCorsOrigins = new List<string>();
codeClient.AllowedCorsOrigins.Add("http://localhost:5003");
codeClient.AllowedScopes = new List<string>();
codeClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
codeClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
codeClient.AllowedScopes.Add("api1");
clients.Add(codeClient);
return clients;
}
Я сделал запрос к IdentityServer4 , как показано на рисунке ниже:
Отображается одно всплывающее окно:
Если я войду, используя username
и password
, я могуполучите access_token
.Но когда я нажал кнопку Google
в Account/Login
, я не смогу получить access token
.
Вот что я получил в Visual Studio ouput
:
IdentityServer4.EntityFramework.Stores.PersistedGrantStore:Debug: P+z5g2/MhMzrbCEGMZOgx06VKeB38F1yx/y3C08vZlo= found in database: False
IdentityServer4.Stores.DefaultAuthorizationCodeStore:Debug: authorization_code grant with value: 4/KQGa0Oj2BztGi86v5SbFD0oVvnXIV8dZWFAQS16O-tyFVOcZXWvRQbByFb18Hhco5SgmkAQtPvk1ZD8lY8eLN9Q not found in store.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 338.1411ms 500 text/html; charset=utf-8
IdentityServer4.Validation.TokenRequestValidator:Error: Invalid authorization code{ code = 4/KQGa0Oj2BztGi86v5SbFD0oVvnXIV8dZWFAQS16O-tyFVOcZXWvRQbByFb18Hhco5SgmkAQtPvk1ZD8lY8eLN9Q }, details: {
"ClientId": "mvc",
"ClientName": "MVC Client",
"GrantType": "authorization_code",
"AuthorizationCode": "4/KQGa0Oj2BztGi86v5SbFD0oVvnXIV8dZWFAQS16O-tyFVOcZXWvRQbByFb18Hhco5SgmkAQtPvk1ZD8lY8eLN9Q",
"Raw": {
"grant_type": "authorization_code",
"code": "4/KQGa0Oj2BztGi86v5SbFD0oVvnXIV8dZWFAQS16O-tyFVOcZXWvRQbByFb18Hhco5SgmkAQtPvk1ZD8lY8eLN9Q",
"redirect_uri": "http://localhost:57547/signin-idsrv",
"client_id": "mvc",
"client_secret": "***REDACTED***"
}
}
Я делаючто-то не так?
Спасибо.