【问题标题】:How to refresh Azure AD B2C identity token如何刷新 Azure AD B2C 身份令牌
【发布时间】:2020-03-17 16:33:26
【问题描述】:

我正在使用带有 OpenIdConnect 的 Azure AD B2C 对 Web 应用程序进行身份验证。我已经让它主要工作了,除了一个小时后身份验证超时,即使用户正在积极使用该应用程序。

这是一个主要使用 ASPX 页面构建的旧 Web 应用程序。我只是在使用身份令牌,并且正在使用 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
            });
    }

我的问题是,这有意义吗?有什么缺点吗?我从未见过任何建议这样做,但这是我发现唯一有效的方法。如果有更好的方法,我想知道它是什么。我考虑将我当前的代码作为“答案”而不是将其包含在问题中,但我不确定它是否正确。

【问题讨论】:

    标签: azure azure-ad-b2c


    【解决方案1】:

    要刷新 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}."
                });
            }
        }
    

    【讨论】:

    • 我的项目中有确切的代码,来自您提供的同一个链接。我的身份令牌永远不会刷新。一小时后,我的请求总是未经身份验证,我必须重定向到 /authorize url,我失去了我正在做的事情。我的 MsalAppBuilder 来自同一个示例,我也在使用示例中的 MsalPerUserMemoryTokenCache。
    • 顺便说一下,您链接到的官方示例与我的网络应用程序的行为方式相同。一小时后,您将未经身份验证并被重定向到 /authorize 端点。如果这就是您所说的“MSAL 将使用缓存的刷新令牌来获取新的 ID 令牌”的意思?这似乎不对。如果您正在做某事,突然间它会将您从 Web 应用程序重定向回来,您可能会丢失您的工作。
    • @Backslider 对于具有授权码流的基于 OWIN 的应用程序,您想要的恐怕是不可能的。在这种情况下,要更新 ID 令牌,它必须通过“OpenID 中间件”,然后通过“Cookie 身份验证中间件”通过 Redirect 设置 cookie。您需要的更像是 SPA 体验。 MSAL.JS 通过 loginPopup 方法支持此方法,但 MSAL.NET 中不支持此方法。
    • @backslider 我遇到了与您描述的完全相同的问题,而且我使用的是相同的代码,但我没有获得刷新令牌,您有没有想过这个问题?
    猜你喜欢
    • 2018-01-08
    • 2018-01-21
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多