Microsoft Graph MVC how to force re-authentication

Viewed 134

I need to force re-authentication of Microsoft Graph within an MVC Core application.

The Graph object is obtained in ConfigureServices using the code segment:

                    var tokenAcquisition = context.HttpContext.RequestServices
                        .GetRequiredService<ITokenAcquisition>();

                    var graphClient = new GraphServiceClient(
                        new DelegateAuthenticationProvider(async (request) => {
                            var token = await tokenAcquisition
                                .GetAccessTokenForUserAsync(_scopes, user: context.Principal);
                            request.Headers.Authorization =
                                new AuthenticationHeaderValue("Bearer", token);
                        })
                    );

The problem is the token goes stale and a later call to Graph fails. Easy to trap and to put in some reauthentication code except it also fails, with a "MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call" error. Plenty of reference to this scenario online but no definitive response that I can find.

Reauthentication code in the controller is:

            if (ex.InnerException.InnerException is MsalUiRequiredException)
            {
                string[] _scopes = _config.GetValue<string>("AzureAd:GraphScopes")?.Split(' ');

                var tokenAcquisition = _http.RequestServices.GetRequiredService<ITokenAcquisition>();

                _graph = new GraphServiceClient(
                    new DelegateAuthenticationProvider(async (request) => 
                    {
                        var options = new TokenAcquisitionOptions() { ForceRefresh = true };
                        var token = await tokenAcquisition.GetAccessTokenForUserAsync(_scopes, user: User, tokenAcquisitionOptions: options);
                        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                    })
                );

            }

Question is how to successfully force reauthentication and obtain a new Graph client?

2 Answers

Answering my own question, turns out it's easily handled in the Controller:

    try
    {
        string token = await _tokenAcquisition
                .GetAccessTokenForUserAsync(GraphConstants.Scopes);
        return View().WithInfo("Token acquired", token);
    }
    catch (MicrosoftIdentityWebChallengeUserException)
    {
        return Challenge();
    }

This code segment is from https://docs.microsoft.com/en-us/graph/tutorials/aspnet-core?tutorial-step=3

I assume that your application is forcing the user to log in and you're using that identity to get a Graph token based on the use of context.Principal:

 .GetAccessTokenForUserAsync(_scopes, user: context.Principal);

When the token expires I assume the original token that was used to get in has also expired about the same time. That means that there is no user and therefore calls fail with the error that you're describing. It makes me think you need to reauthenticate before you try to get a new graph token.

However, you should monitor the token and get a new one just before it expires - silently -using the refresh token rather than a new authentication.

https://docs.microsoft.com/en-us/advertising/guides/authentication-oauth-get-tokens?view=bingads-13#refresh-accesstoken

Related