Теперь мне удалось настроить приложение IdentityServer и отдельное клиентское приложение MVC, где IdentityServer использует пользовательскую базу данных приложения MVC. Приложение MVC используется для регистрации пользователей, входа в систему, выхода из системы и вызова дополнительных API.
Я могу войти в приложение MVC, а также получить верный токен от IdentityServer. Но сейчас у меня две проблемы:
- Токен в HttpContext имеет значение null, хотя IdentityServer успешно выдает токен. Но оба следующих варианта не работают, когда я пытаюсь получить доступ к токену из HttpContext:
var accessToken = await HttpContext.GetTokenAsync("access_token");
var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");
Может быть причина в том, что я запускаю оба проекта с помощью "запуска по сети", а не с IIS в VS2017?
- Где или как сохранить токен для клиента для будущих вызовов API? В идеале токен отправляется автоматически при каждом запросе. Я забыл конфигурацию здесь?
Вот так выглядят мои конфигурации:
Приложение IdentityServer:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
Config.cs
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
};
}
Клиентское приложение MVC:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
options.ClaimActions.MapJsonKey("website", "website");
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
Тестовый контроллер
public async Task<IActionResult> LoginTest()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
}
var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("myusername", "mypassword");
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
//var accessToken = await HttpContext.GetTokenAsync("access_token");
var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");
return View();
}
tokenResponse успешно заполнен:
info: IdentityServer4.Events.DefaultEventService[0]
{
"Name": "Token Issued Success",
"Category": "Token",
"EventType": "Success",
"Id": 2000,
"ClientId": "mvc",
"ClientName": "MVC Client",
"Endpoint": "Token",
"SubjectId": "08be5c35-5593-49f4-999b-e5ddd694f2e9",
"Scopes": "openid profile api1 offline_access",
"GrantType": "password",
"Tokens": [
{
"TokenType": "refresh_token",
"TokenValue": "****55a0"
},
{
"TokenType": "access_token",
"TokenValue": "****YuFw"
}
],
"ActivityId": "0HLMAGC28PO61:00000001",
"TimeStamp": "2019-04-26T20:09:27Z",
"ProcessId": 21208,
"LocalIpAddress": "::1:5000",
"RemoteIpAddress": "::1"
}
Я новичок в IdentityServer, поэтому я разработал SPA с WebAPI и сохранил JWT в локальном хранилище. Я думаю, что есть более удобный способ сделать это с клиентом MVC и IdentityServer.