access token / refresh token with MSAL

2020-07-18 11:45发布

问题:

I'm moderately familiar with OAuth2 and the concepts of the AccessToken and RefreshToken.

It looks like MSAL is doing some work for us when using ClientApplicationBase.AcquireTokenSilentAsync().

I'm not clear as to whether it will always check the expiration of the current AccessToken and automatically refresh it (using the RefreshToken) on method call.

Regardless, is there a "best practice" for how often we should call AcquireTokenSilentAsync() ? Should we keep track of the expiration ourselves and call this method to update our bearer authentication header? Should we be calling AcquireTokenSilentAsync() on every Request? (doubtful)

I can't see how the GraphServiceClient (tangent topic, I know) using the DelegateAuthenticationProvider will do anything helpful WRT refreshing. Do we need to extend that class and perform our own refresh when the token is nearing expiration? I feel like this would/should be already in the SDK.

Thanks for any tips. -AJ

回答1:

I'm not clear as to whether it will always check the expiration of the current AccessToken and automatically refresh it (using the RefreshToken) on method call.

A refresh token is automatically supplied when the offline_access scope is provided, if I understand this answer correctly

...you've requested the offline_access scope so your app receives a Refresh Token.

The description of AcquireTokenSilentAsync implies that when an refresh token is provided, it will check the expiration date on the token, and get a new one if it's expired or close to expiring.

If access token is expired or close to expiration (within 5 minute window), then refresh token (if available) is used to acquire a new access token by making a network call.

It will repeat this behavior until the refresh token is expired. Optionally you can force a refresh of the access token via the refresh token by utilizing the forceRefresh parameter on AcquireTokenSilentAsync

Lastly, I am going to quote this answer on SO since it gives a nice insight about MSAL and tokens

Just to make a small clarification, MSAL doesn't actually issue tokens or decide a token expiration, but rather ingests an acquires token from the Azure AD STS.

MSAL will automatically refresh your access token after expiration when calling AcquireTokenSilentAsync. .... The default token expirations right now are:

Access Tokens: 1 hour

Refresh Tokens: 90 days, 14 day inactive sliding window

(June 13th '17)

Regardless, is there a "best practice" for how often we should call AcquireTokenSilentAsync() ? Should we keep track of the expiration ourselves and call this method to update our bearer authentication header? Should we be calling AcquireTokenSilentAsync() on every Request?

The documentation also lists a 'Recommended call pattern' for calling the AcquireTokenSilentAsync. The documentation also mentions that

For both Public client and confidential client applications, MSAL.NET maintains a token cache (or two caches in the case of confidential client applications), and applications should try to get a token from the cache first before any other means.

Based on examples I've seen, including the recommended call pattern from the documentation, I would argue you could simply call AcquireTokenSilentAsyncand catch the MsalUiRequiredException as an indication that the token has expired and the user has to log in again.

I can't see how the GraphServiceClient (tangent topic, I know) using the DelegateAuthenticationProvider will do anything helpful WRT refreshing. Do we need to extend that class and perform our own refresh when the token is nearing expiration? I feel like this would/should be already in the SDK.

If I understand the DelegateAuthenticationProvider correctly, what it does is modify the requestMessage before we pass it to Graph. All we got to do is provide our access token with an authorization header for the request. We already know that when we fetch our access token, it is valid, so we can just add it.

        new DelegateAuthenticationProvider(async (requestMessage) =>
        {
            ConfidentialClientApplication cca = new ConfidentialClientApplication(_ClientId, _Authority, _RedirectUri, new ClientCredential(_ClientSecret), _UserTokenSessionCache.GetTokenCache(identifier, httpContext), _ApplicationTokenCache.GetTokenCache());
            AuthenticationResult result = await cca.AcquireTokenSilentAsync();
            requestMessage.Headers.Add("Authorization", result.CreateAuthorizationHeader());
            //OR
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
        });

(There is no difference between either way of setting the headers)

I've been down this path and this does the trick for me. I highly advise reading their documentation, because it does gives a good insight in how to implement MSAL.Net.

I haven't had time yet to play around with the token durations yet. Nor the behavior if no refresh token is provided (if that's even possible)

I hope this helps!



回答2:

Mentioning one thing missed above, quoting my answer to Get refresh token with Azure AD V2.0 (MSAL) and Asp .Net Core 2.0

  • For context, OAuth 2.0 code grant flow mentions the following steps:
    • authorization, which returns auth_code
    • using auth_code, to fetch access_token (usually valid for 1 hr) and refresh_token
    • access_token is used to gain access to relevant resources
    • after access_token expires, refresh_token is used to get new access_token
  • MSAL.NET abstracts this concept of refresh_token via TokenCache.
    • There is an option to serialize TokenCache. See Token cache serialization in MSAL.NET. This is how to preserve sign-in info b/w desktop application sessions, and avoid those sign-in windows.
    • AcquireTokenSilentAsync is the process by which refresh_token is used to get new access_token, but, this is internally done. See AcquireTokenSilentAsync using a cached token for more details and other access patterns.

Hope this clarifies on why TokenCache is the 'new' refresh_token in MSAL.NET, and TokenCache is what you would need to serialize and save. There are libraries like Microsoft.Identity.Client.Extensions.Msal that aid in this.

@AlWeber/ @Raziel, the following pattern would apply for PublicClientApplication:

  • on startup, to deserialization and load TokenCache (which has refresh_token), try acquire access_token silently.
  • if that fails, use interactive UI to fetch token.
  • save and re-use the AuthenticationResult, which has AccessToken and ExpiresOn. Redo acquire access_token silently (bit expensive if you are an API user, hence caching of result), once you are close to ExpiresOn property (personally, I set 30 min before expiry).

is there a "best practice" for how often we should call AcquireTokenSilentAsync() ? Should we keep track of the expiration ourselves and call this method to update our bearer authentication header? Should we be calling AcquireTokenSilentAsync() on every Request? (doubtful)

I don't think this is a good idea. As mentioned, this call is still a bit expensive. Alternative, is to store AuthenticationResult in-memory, re-use it, and go to silent acquire workflow only close to ExpiresOn property.



标签: msal