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;
}
}