У меня есть стандартные настройки .NET Core 2.1 (MVC и API) и Identity Server 4.Я использую эталонные токены вместо токенов jwt.
Сценарий выглядит следующим образом:
- Просмотрите мое приложение
- Перенаправлено на Identity Server
- Введите действительные действительные учетные данные
- Перенаправлено обратно в приложение со всеми утверждениями (ролями) и правильным доступом к приложению и API
Подождите неопределенное количество времени (думаю, это час, У меня нет точного времени)
- Просмотрите мое приложение
- Перенаправлено на Identity Server
- Я все еще вошел в IDP, поэтому я сразу перенаправлен обратно в свое приложение
- На этом этапе вошедший в систему пользователь .NET пропускает утверждения (роли) и больше не имеет доступа к API
Тот же самый результат происходит, если я удаляю все файлы cookie приложения
Мне кажется очевидным, что токен доступа истек.Как мне справиться с этим сценарием?Я все еще вошел в IDP, и промежуточное программное обеспечение автоматически зарегистрировало меня в моем приложении, однако с маркером доступа с истекшим сроком действия (?) И пропущенными заявками.
Это как-то связано с использованием эталонных токенов?
Я копаюсь в огромном беспорядке тем и статей, любых указаниях и / или решениях этого сценария?
РЕДАКТИРОВАТЬ : Похоже, мой токен доступа действителен,Я сузил свою проблему до недостающих данных профиля пользователя.Конкретно на роль претендуют.
Когда я очищаю свое приложение и куки-файлы IDP, все работает нормально.Однако после периода «x» (1 час?), Когда я пытаюсь обновить или получить доступ к приложению, меня перенаправляют в IDP, а затем обратно в приложение.
На данный момент у меня есть действительный и аутентифицированный пользователь, однако я пропускаю все свои заявки на роль.
Как настроить промежуточное программное обеспечение AddOpenIdConnect для извлечения недостающих утверждений в этом сценарии?
Я предполагаю, что в событии OnUserInformationReceived я могу проверить отсутствие пропущенного утверждения "роль", если пропущено, затем вызвать UserInfoEndpoint ... который выглядит как очень странный рабочий процесс.Тем более, что при «новом» входе в систему «роль» возвращается нормально.(Примечание. В сценарии ошибок утверждение о роли отсутствует в контексте).
Вот моя конфигурация клиентского приложения:
services.AddAuthentication(authOpts =>
{
authOpts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
authOpts.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, opts => { })
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, openIdOpts =>
{
openIdOpts.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
openIdOpts.Authority = settings.IDP.Authority;
openIdOpts.ClientId = settings.IDP.ClientId;
openIdOpts.ClientSecret = settings.IDP.ClientSecret;
openIdOpts.ResponseType = settings.IDP.ResponseType;
openIdOpts.GetClaimsFromUserInfoEndpoint = true;
openIdOpts.RequireHttpsMetadata = false;
openIdOpts.SaveTokens = true;
openIdOpts.ResponseMode = "form_post";
openIdOpts.Scope.Clear();
settings.IDP.Scope.ForEach(s => openIdOpts.Scope.Add(s));
// https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/
// https://github.com/aspnet/Security/issues/1449
// https://github.com/IdentityServer/IdentityServer4/issues/1786
// Add Claim Mappings
openIdOpts.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username"); /* SID alias */
openIdOpts.ClaimActions.MapJsonKey("role", "role", "role");
openIdOpts.TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = settings.IDP.ClientId,
ValidIssuer = settings.IDP.Authority,
NameClaimType = "name",
RoleClaimType = "role"
};
openIdOpts.Events = new OpenIdConnectEvents
{
OnUserInformationReceived = context =>
{
Log.Info("Recieved user info from IDP.");
// check for missing roles? they are here on a fresh login but missing
// after x amount of time (1 hour?)
return Task.CompletedTask;
},
OnRedirectToIdentityProvider = context =>
{
Log.Info("Redirecting to identity provider.");
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
Log.Debug("OnTokenValidated");
// this addressed the scenario where the Identity Server validates a user however that user does not
// exist in the currently configured source system.
// Can happen if there is a configuration mismatch between the local SID system and the IDP Client
var validUser = false;
int uid = 0;
var identity = context.Principal?.Identity as ClaimsIdentity;
if (identity != null)
{
var sub = identity.Claims.FirstOrDefault(c => c.Type == "sub");
Log.Debug($" Validating sub '{sub.Value}'");
if (sub != null && !string.IsNullOrWhiteSpace(sub.Value))
{
if (Int32.TryParse(sub.Value, out uid))
{
using (var configSvc = ApiServiceHelper.GetAdminService(settings))
{
try
{
var usr = configSvc.EaiUser.GetByID(uid);
if (usr != null && usr.ID.GetValueOrDefault(0) > 0)
validUser = true;
}
catch { }
}
}
}
Log.Debug($" Validated sub '{sub.Value}'");
}
if (!validUser)
{
// uhhh, does this work? Logout?
// TODO: test!
Log.Warn($"Unable to validate user is SID for ({uid}). Redirecting to '/Home/Logout'");
context.Response.Redirect("/Home/Logout?msg=User not validated in source system");
context.HandleResponse();
}
return Task.CompletedTask;
},
OnTicketReceived = context =>
{
// TODO: Is this necessary?
// added the below code because I thought my application access_token was expired
// however it turns out I'm actually misisng the role claims when I come back to the
// application from the IDP after about an hour
if (context.Properties != null &&
context.Properties.Items != null)
{
DateTime expiresAt = System.DateTime.MinValue;
foreach (var p in context.Properties.Items)
{
if (p.Key == ".Token.expires_at")
{
DateTime.TryParse(p.Value, null, DateTimeStyles.AdjustToUniversal, out expiresAt);
break;
}
}
if (expiresAt != DateTime.MinValue &&
expiresAt != DateTime.MaxValue)
{
// I did this to synch the .NET cookie timeout with the IDP access token timeout?
// This somewhat concerns me becuase I thought that part should be done auto-magically already
// I mean, refresh token?
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = expiresAt;
}
}
return Task.CompletedTask;
}
};
});