我有一个标准的.NET Core 2.1(MVC和API)和Identity Server 4项目设置。 我使用的是参考令牌,而不是jwt令牌。
方案如下:
等待不确定的时间(我认为是一个小时,我没有确切的时间)
如果我删除所有应用程序cookie,也会发生相同的结果
在我看来,访问令牌已过期。如何处理这种情况?我仍然登录到IDP,并且中间件自动将我登录到我的应用程序,但是,访问令牌已过期(?)并且缺少声明。
这与引用标记的使用有关吗?
我正在研究大量的线索和文章,对此场景有任何指导和/或解决方案?
编辑:看来我的访问令牌有效。我已将问题缩小到缺少的用户个人资料数据。具体来说,角色声明。
当我同时清除应用程序和IDP Cookie时,一切正常。但是,在“ 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;
}
};
});
答案 0 :(得分:0)
对不起大家,看来我找到了问题的根源。
我总失败了:(。
我的Identity Server实现中的ProfileService中有一个错误,该错误导致在所有情况下都不会返回角色
哼,谢谢!