У нас есть требование для аутентификации пользователей в IdentityServer4 по внешнему API. Сценарий работает следующим образом:
- Пользователь посещает клиентское приложение Javascript и нажимает кнопку входа в систему, чтобы перенаправить на страницу входа в IdentityServer (точно такой же клиент, как указано в документации здесь
Пользователь вводит свое имя пользователя (адрес электронной почты) и пароль
IdentityServer4 подключается к внешнему API для проверки учетных данных
Пользователь перенаправляется обратно в приложение JavaScript
Вышеописанный процесс отлично работает при использовании TestUsers, представленных в QuickStarts. Однако при использовании API страница входа в систему сбрасывается и не перенаправляет пользователя обратно на клиент JavaScript. Единственное изменение - это приведенный ниже код и пользовательская реализация IProfileService.
Ниже приведен пользовательский код в действии входа в систему (отображается только соответствующая часть):
var apiClient = _httpClientFactory.CreateClient("API");
var request = new HttpRequestMessage(HttpMethod.Post, "/api/auth");
var loginModel = new LoginModel
{
Email = model.Email,
Password = model.Password
};
var content = new StringContent(JsonConvert.SerializeObject(loginModel),
Encoding.UTF8, "application/json");
request.Content = content;
HttpResponseMessage result = await apiClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
var loginStatus = JsonConvert.DeserializeObject<ApiLoginStatus>(
await result.Content.ReadAsStringAsync());
if (loginStatus.LoginSuccess)
{
await _events.RaiseAsync(new UserLoginSuccessEvent(model.Email, model.Email, loginStatus.Name, clientId: context?.ClientId));
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
var user = new IdentityServerUser(loginStatus.SubjectId)
{
DisplayName = loginStatus.Name
};
await HttpContext.SignInAsync(user, props);
if (context != null)
{
if (await _clientStore.IsPkceClientAsync(context.ClientId))
{
return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl });
}
return Redirect(model.ReturnUrl);
}
Код фактически соответствует пути возврата View (), но по какой-то причине он сбрасывается, и страница входа снова отображается.
Код в Startu p.cs:
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients)
.AddProfileService<ProfileService>()
.AddDeveloperSigningCredential();
Код в ProfileService.cs:
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var profile = await GetUserProfile(context.Subject.GetSubjectId());
var claims = new List<Claim>
{
new Claim(ClaimTypes.Email, profile.Email),
new Claim(ClaimTypes.Name, profile.Name)
};
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context)
{
var profile = await GetUserProfile(context.Subject.GetSubjectId());
context.IsActive = (profile != null);
}
В Интернете есть несколько источников, показывающих, как использовать пользовательское хранилище для аутентификации, но все они, похоже, используют ResourceOwnerPasswordValidator. Если бы кто-то мог указать на то, чего здесь не хватает, это очень помогло бы. Спасибо.