在 AJAX 调用之前从 MSAL 刷新令牌的正确方法?

Posted

技术标签:

【中文标题】在 AJAX 调用之前从 MSAL 刷新令牌的正确方法?【英文标题】:Correct way to Refresh a token from MSAL before an AJAX call? 【发布时间】:2021-08-31 21:27:27 【问题描述】:

有没有办法让 MSAL 在 AJAX 请求之前使用刷新令牌刷新访问令牌?

我的代码设置与此处相同: https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect/blob/master/WebApp/App_Start/Startup.Auth.cs

using Microsoft.Identity.Client;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Host.SystemWeb;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using WebApp.Utils;

namespace WebApp

    public partial class Startup
    
        public void ConfigureAuth(IAppBuilder app)
        
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions());

            // Custom middleware initialization. This is activated when the code obtained from a code_grant is present in the querystring (&code=<code>).
            app.UseOAuth2CodeRedeemer(
                new OAuth2CodeRedeemerOptions
                
                    ClientId = AuthenticationConfig.ClientId,
                    ClientSecret = AuthenticationConfig.ClientSecret,
                    RedirectUri = AuthenticationConfig.RedirectUri
                );

            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                
                    // The `Authority` represents the v2.0 endpoint - https://login.microsoftonline.com/common/v2.0
                    Authority = AuthenticationConfig.Authority,
                    ClientId = AuthenticationConfig.ClientId,
                    RedirectUri = AuthenticationConfig.RedirectUri,
                    PostLogoutRedirectUri = AuthenticationConfig.RedirectUri,
                    Scope = AuthenticationConfig.BasicSignInScopes + " Mail.Read", // a basic set of permissions for user sign in & profile access "openid profile offline_access"
                    TokenValidationParameters = new TokenValidationParameters
                    
                        ValidateIssuer = false,
                        // In a real application you would use IssuerValidator for additional checks, like making sure the user's organization has signed up for your app.
                        //     IssuerValidator = (issuer, token, tvp) =>
                        //     
                        //        //if(MyCustomTenantValidation(issuer))
                        //        return issuer;
                        //        //else
                        //        //    throw new SecurityTokenInvalidIssuerException("Invalid issuer");
                        //    ,
                        //NameClaimType = "name",
                    ,
                    Notifications = new OpenIdConnectAuthenticationNotifications()
                    
                        AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                        AuthenticationFailed = OnAuthenticationFailed,
                        RedirectToIdentityProvider = OnRedirectToIdentityProvider,
                    ,
                    // Handling SameSite cookie according to https://docs.microsoft.com/en-us/aspnet/samesite/owin-samesite
                    CookieManager = new SameSiteCookieManager(
                                     new SystemWebCookieManager())
                );
        

        private  Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> arg)
        
            arg.ProtocolMessage.SetParameter("myNewParameter", "its Value");
            return Task.CompletedTask;
        

        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
        
            // Upon successful sign in, get the access token & cache it using MSAL
            IConfidentialClientApplication clientApp = MsalAppBuilder.BuildConfidentialClientApplication();
            AuthenticationResult result = await clientApp.AcquireTokenByAuthorizationCode(new[]  "Mail.Read" , context.Code).ExecuteAsync();
        

        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        
            notification.HandleResponse();
            notification.Response.Redirect("/Error?message=" + notification.Exception.Message);
            return Task.FromResult(0);
        
    


我尝试通过登录挑战重定向用户,但立即遇到了 cors 问题。我尝试调用 aquiretokenAsync() 以便 MSAL 将在幕后使用刷新令牌,但这似乎也导致了 cors 问题。 (如果是这样的话,我该怎么称呼它?我可以在授权属性中这样做吗?或者最好的地方在哪里?)

我已经搜索了所有我能找到的堆栈溢出和 Azure github 问题,但要么无法理解答案,要么它们似乎已经 5 岁了,并且使用 ADAL 而不是 MSAL。

例如 Handle token timeout in Asp.Net MVC when using Azure AD

Refresh token before time out Angular

https://github.com/azuread/microsoft-identity-web/issues/603

tl;博士

我知道如果我执行 aquireTokenAsync,MSAL 将在内部自动刷新,但我不清楚何时何地可以在 AJAX 请求之前调用此函数且无需重新加载页面。

我是 MSAL 的新手,一般来说是 OAUTH2 身份验证,非常感谢任何解释

【问题讨论】:

【参考方案1】:

您可以使用acquireTokenSilent 方法在 MSAL.NET 中为您自动刷新令牌。

MSAL 使用缓存来存储基于特定参数的令牌 包括范围、资源和权限,并将检索令牌 需要时从缓存中获取。它还可以执行无声更新 这些令牌已过期。 MSAL 公开了此功能 通过acquireTokenSilent方法。

acquireTokenSilent 会在缓存中寻找一个有效的令牌,如果它 即将到期或不存在,将自动尝试 为你刷新。您应该使用 loginXXXX 或 acquireTokenXXXXXX (交互式)API 在此之前建立与服务器的会话。

AuthenticationResult result = null;
var accounts = await app.GetAccountsAsync();

try

 result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
        .ExecuteAsync();

catch (MsalUiRequiredException ex)

 // A MsalUiRequiredException happened on AcquireTokenSilent.
 // This indicates you need to call AcquireTokenInteractive to acquire a token
 System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: ex.Message");

 try
 
    result = await app.AcquireTokenInteractive(scopes)
          .ExecuteAsync();
 
 catch (MsalException msalex)
 
    ResultText.Text = $"Error Acquiring Token:System.Environment.NewLinemsalex";
 

catch (Exception ex)

 ResultText.Text = $"Error Acquiring Token Silently:System.Environment.NewLineex";
 return;


if (result != null)

 string accessToken = result.AccessToken;
 // Use the token

【讨论】:

以上是关于在 AJAX 调用之前从 MSAL 刷新令牌的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak:ajax调用过期后刷新访问令牌

MSAL Angular:如何从 JWT 令牌中检索用户角色?

如果用户从 msal .net 代码登录,则无法使用 msal.js 静默获取访问令牌

在 AngularJS SPA 中使用刷新令牌

刷新 OAuth2 访问令牌的正确范例

JavaScript AJAX 调用中的安全 REST API 令牌