Microsoft Graph API multi-tenant token lifetime

596 Views Asked by At

I am currently using this example to log onto Microsoft graph in C#. I am not sure what kind of token this uses internally, so my main question is : Do I have to worry about timing out of refresh tokens? I found this page about configuring refresh token lifetimes - do I need to change these? And if I change them in the applications tenant, will they also be set in the tenants I register my application on?

The initial authentication works like this (full code here) :

 AuthorizationCodeReceived = async (context) =>
                        {
                            // We received an authorization code => get token.
                            var code = context.Code;

                            ClientCredential credential = new ClientCredential(clientId, appKey);
                            string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
                            string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                            Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(string.Format("https://login.microsoftonline.com/{0}", tenantID), new EFADALTokenCache(signedInUserID));
                            AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
                                code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path).TrimEnd('/')), credential, graphResourceID);
                        }

I modified the authorization process a bit, so I can use the methods from https://github.com/Azure-Samples/active-directory-dotnet-graphapi-web :

internal class AuthenticationHelper
    {

        /// <summary>
        ///     Async task to acquire token for Application.
        /// </summary>
        /// <returns>Async Token for application.</returns>
        public static async Task<string> AcquireTokenAsync()
        {
            string clientId = ConfigurationManager.AppSettings["ida:ClientID"];
            string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
            string graphResourceID = Constants.ResourceUrl;
            string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
            string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

            // get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
            ClientCredential clientcred = new ClientCredential(clientId, appKey);
            // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
            AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.microsoftonline.com/{0}", tenantID), new EFADALTokenCache(signedInUserID));
            AuthenticationResult result = await authContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
            return result.AccessToken;
        }

        /// <summary>
        ///     Get Active Directory Client for Application.
        /// </summary>
        /// <returns>ActiveDirectoryClient for Application.</returns>
        public static ActiveDirectoryClient GetActiveDirectoryClient()
        {
            string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            Uri baseServiceUri = new Uri(Constants.ResourceUrl);
            ActiveDirectoryClient activeDirectoryClient =
                new ActiveDirectoryClient(new Uri(baseServiceUri, tenantID),
                    async () => await AcquireTokenAsync());
            return activeDirectoryClient;
        }
    }

The (Bearer) AccessToken I get from the above code has an ExpirationDate that equals the time of the request and is quite long - so what really happens during the authentication process seems like a black box for me and I would love to understand it.

1

There are 1 best solutions below

1
On

Short answer: yes.

Long answer: The library you are using (ADAL) will do everything it can to keep your refresh tokens (and therefore your user's session) valid for as long as possible. It will handle token refresh for you every time you make a call to AcquireToken*. However, eventually refresh tokens will expire. When exactly they expire is out of your control (as the developer). You can set defaults using the link you refer to, but those defaults can always be overridden by customers. Refresh tokens can also become invalid for other reasons, like if the user revokes your app or your app is identified to be potentially malicious.

So, you need to handle the case where ADAL cannot get a token for you. In ADAL, this will result in an exception that you can handle gracefully and redirect the user to sign in again, where they can remedy whatever situation caused the refresh token to expire.