我正在将Azure AD B2C与OpenIdConnect一起用于Web应用程序的身份验证。我的主要工作原理是,即使用户正在积极使用该应用程序,身份验证也会在一个小时后超时,
这是一个旧的Webapp,主要使用ASPX页面构建。我只是使用一个身份令牌,而我正在使用cookie。我的应用程序中根本没有使用访问令牌。根据用户声明,访问以预先存在的方式进行。我正在使用Microsoft.Identity.Client中的MSAL.Net库。登录正常。我找回一个代码,然后将其交换为身份令牌
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
一切正常,但不管我做什么,令牌都将在1小时后失效。即使我正在使用该应用程序,一个小时后的第一个请求也将未经身份验证。我尝试添加一个呼叫以静默方式获取令牌,以查看是否可以刷新它,但事实并非如此。使用OpenIdConnect,始终包含offline_access范围。如果我尝试明确包含它,则会抛出一个错误。但是我从未见过任何证据表明即使在幕后也有刷新令牌。
我在StackOverflow-Azure AD B2C OpenID Connect Refresh token上发现了这个问题,第一个答案引用了一个称为UseTokenLifetime的OpenIdConnect属性。如果将其设置为false,那么一个小时后我不会丢失身份验证,但是现在相反。令牌/ cookie似乎永远不会过期,我可以永远保持登录状态。
我的愿望是,只要用户正在积极使用该应用程序,他们便会保持登录状态,但是如果他们在一段时间(一个小时)内停止使用该应用程序,则必须重新进行身份验证。我找到了一种方法,可以通过数小时的反复试验来实现这一目标,但我不确定这是否有意义和/或安全。我现在要做的是,在每个经过身份验证的请求上,我都会更新用户的“ exp”声明(不确定是否重要),然后生成新的AuthenticationResponseGrant,将ExpiresUtc设置为新时间。在测试中,如果我在不到一个小时的时间内输入此代码,它将使我保持登录状态,然后,如果我等待一个小时以上,则将不再通过身份验证。
HttpContext.Current.User.SetExpirationClaim(DateTime.Now.AddMinutes(60.0));
public static void SetExpirationClaim(this IPrincipal currentPrincipal, DateTime expiration)
{
System.Diagnostics.Debug.WriteLine("Setting claims expiration to {0}", expiration);
int seconds = (int)expiration.Subtract(epoch).TotalSeconds;
currentPrincipal.AddUpdateClaim("exp", seconds.ToString(), expiration);
}
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value, DateTime expiration)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
identity.AddClaim(new Claim(key, value));
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity),
new AuthenticationProperties() {
IsPersistent = true,
ExpiresUtc = new DateTimeOffset(expiration).UtcDateTime,
IssuedUtc = new DateTimeOffset(DateTime.Now).UtcDateTime
});
}
我的问题是,这有意义吗?有什么缺点吗?我从未见过任何建议可以这样做,但是这是我发现唯一可行的方法。如果有更好的方法,我想知道它是什么。我考虑过将当前代码设为“答案”,而不是将其包含在问题中,但我不确定它是否正确。
答案 0 :(得分:0)
要刷新ID令牌,您需要使用刷新令牌。刷新令牌对客户端而言是不透明的,但可以由MSAL缓存。然后,当ID令牌过期时,MSAL将使用缓存的刷新令牌来获取新的ID令牌。
但是,您需要按照official sample中的指示,自己实现缓存逻辑。
核心代码片段:
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
try
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = $"Unable to get authorization code {ex.Message}."
});
}
}