1
votes

I am setting up a multi tenant application and I am having issues creating a GraphServiceClient.

I have to following AuthorizationCodeReceived:

AuthorizationCodeReceived = async context =>
                    {
                        var tenantId =
                            context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

                        var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/"+ tenantId);
                        await authenticationContext.AcquireTokenByAuthorizationCodeAsync(
                            context.Code,
                            new Uri("http://localhost:21925"),
                            new ClientCredential(ClientId, ClientSecret),
                            "https://graph.microsoft.com");
                    }

This works perfectly to authenticate the user. I am using fiddler, and I see that a new bearer token was given by login.microsoftonline.com/{tenantid}/oauth2/token

When creating a new Graph Service Client I use the following factory method:

public IGraphServiceClient CreateGraphServiceClient()
    {
        var client = new GraphServiceClient(
            new DelegateAuthenticationProvider(
                async requestMessage =>
                {
                    string token;
                    var currentUserId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
                    var currentUserHomeTenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
                    var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/" + currentUserHomeTenantId + "/");
                    var clientCredential = new ClientCredential(_configuration.ClientId, _configuration.ClientSecret);

                    try
                    {
                        var authenticationResult = await authenticationContext.AcquireTokenSilentAsync(
                            GraphResourceId,
                            clientCredential,
                            new UserIdentifier(currentUserId, UserIdentifierType.UniqueId));

                        token = authenticationResult.AccessToken;
                    }
                    catch (AdalSilentTokenAcquisitionException e)
                    {
                        var result = await authenticationContext.AcquireTokenAsync(GraphResourceId, clientCredential);
                            token = result.AccessToken;
                    }

                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
                }));

        return client;
    }

This method always throws an AdalSilentAcquisitionException and the AcquireTokenAsync retrieves a new token.

With this token, I am not able to request 'Me' on the graph. I get the following exception: message=Resource 'some guid' does not exist or one of its queried reference-property objects are not present.

However, if I am debugging and I change the token before it is passed to the header, with the value of the one I got previously right after login in (received from login.microsoftonline.com/{tenantid}/oauth2/token ) then the API call works.

Does anyone know what I am doing wrong? He can I get the acquiretokensilently working?

Update: I have updated the code samples. I have removed the custom cache, and now everything seems to work.

How can I make a custom cache based on the http sessions, making sure the AcquireTokenSilently works.

Preview of not working token cache:

public class WebTokenCache : TokenCache
{
    private readonly HttpContext _httpContext;
    private readonly string _cacheKey;
    public WebTokenCache()
    {
        _httpContext = HttpContext.Current;
        var claimsPrincipal = (ClaimsPrincipal) HttpContext.Current.User;
        _cacheKey = BuildCacheKey(claimsPrincipal);

        AfterAccess = AfterAccessNotification;

        LoadFromCache();
    }

    private string BuildCacheKey(ClaimsPrincipal claimsPrincipal)
    {
        var clientId = claimsPrincipal.FindFirst("aud").Value;
        return $"{claimsPrincipal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value}_TokenCache";
    }

    private void LoadFromCache()
    {
        var token = _httpContext.Cache[_cacheKey];
        if (token == null) return;

        Deserialize((byte[]) token);
    }

    private void AfterAccessNotification(TokenCacheNotificationArgs args)
    {
        if (!HasStateChanged) return;

        if (Count > 0)
        {
            _httpContext.Cache[_cacheKey] = Serialize();
        }
        else
        {
            _httpContext.Cache.Remove(_cacheKey);
        }

        HasStateChanged = false;
    }
}
1

1 Answers

1
votes

I am trying use the code above and it works well form me.

Please ensure that the GraphResourceId is https://graph.microsoft.com(This resource is requested first time in your startUp class) since the method AcquireTokenSilentAsync will try to retrieve the token from cache based on the resrouce.