pinewood
pinewood

Reputation: 1839

Failed to acquire token silently - Microsoft Graph API

We are working on an ASP.NET MVC project using the Microsoft Graph API. It is heavily based on the sample code at https://github.com/microsoftgraph/aspnet-snippets-sample. The app works fine at first – we can login using our tenant account and get data from the graph. However, if we start a new session from Visual studio or if we just wait a while, AcquireTokenSilentAsync throws

Failed to acquire token silently

If the web browser cache is cleared it works again, but after a while the error returns. We have tried changing the authority to common, organizations and tenant but the error remains.

Our method for getting the access token is as follows:

 // Gets an access token and its expiration date. First tries to get the token from the token cache.
    public async Task<string> GetUserAccessTokenAsync()
    {
        // Initialize the cache.
        HttpContextBase context = HttpContext.Current.GetOwinContext().Environment["System.Web.HttpContextBase"] as HttpContextBase;

        tokenCache = new SessionTokenCache(
            ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value,
            context);
        IEnumerable<TokenCacheItem> cachedItems = tokenCache.ReadItems(appId); // see what's in the cache
        if (cachedItems.Count() > 0)
            return cachedItems.First().Token;

        if (!redirectUri.EndsWith("/")) redirectUri = redirectUri + "/";
        string[] segments = context.Request.Path.Split(new char[] { '/' });
        ConfidentialClientApplication cca = new ConfidentialClientApplication(
            appId,
            redirectUri + segments[1],
            new ClientCredential(appSecret),
            tokenCache);

        string allScopes = nonAdminScopes;
        string[] scopes = allScopes.Split(new char[] { ' ' });
        try
        {
            AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes);
            return result.Token;
        }

        // Unable to retrieve the access token silently.
        catch (MsalSilentTokenAcquisitionException)
        {
            HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
              new AuthenticationProperties() { RedirectUri = redirectUri + segments[1] },
              OpenIdConnectAuthenticationDefaults.AuthenticationType);

            throw new ServiceException(
                new Error
                {
                    Code = GraphErrorCode.AuthenticationFailure.ToString(),
                    Message = Resource.Error_AuthChallengeNeeded,
                });
        }
    }

Any ideas why the token can't be acquired silently?

Upvotes: 0

Views: 1232

Answers (1)

awnton
awnton

Reputation: 659

How do you handle the exception thrown? In the sample you provided they handle the error with a try/catch and if the exception is raised and the error message matches they return an empty result. This request will then be intercepted by the OpenId middleware (because ..GetOwinContext().Authentication.Challenge() sets the response code to 401 and authentication type to OpenIdConnectAuthenticationDefaults.AuthenticationType).

        try
        {

            // Initialize the GraphServiceClient.
            GraphServiceClient graphClient = SDKHelper.GetAuthenticatedClient();

            // Get the files and folders in the current user's drive.
            results.Items = await filesService.GetMyFilesAndFolders(graphClient);
        }
        catch (ServiceException se)
        {
            if (se.Error.Message == Resource.Error_AuthChallengeNeeded) return new EmptyResult();
            return RedirectToAction("Index", "Error", new { message = string.Format(Resource.Error_Message, Request.RawUrl, se.Error.Code, se.Error.Message) });
        }

Upvotes: 1

Related Questions