I am working with MSAL and have a user who received the following error:
{
"error":{
"code":"ResourceNotFound",
"message":"Resource could not be discovered.",
"innerError":{
"request-id":"99b44a33-e5cd-4b69-9730-32d72e1f4ebf",
"date":"2016-12-11T03:51:37"
}
}
}
The code is the default MSAL demo code:
public async Task<ActionResult> ReadMail()
{
try
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
ConfidentialClientApplication cca = new ConfidentialClientApplication(clientId, null,
new ClientCredential(appKey), new MSALSessionCache(signedInUserID, this.HttpContext));
string[] scopes = { "Mail.Read" };
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes);
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", result.Token);
HttpResponseMessage hrm = await hc.GetAsync("https://graph.microsoft.com/v1.0/me/messages");
string rez = await hrm.Content.ReadAsStringAsync();
ViewBag.Message = rez;
return View();
}
catch (MsalSilentTokenAcquisitionException)
{
ViewBag.Relogin = "true";
return View();
}
catch (Exception eee)
{
ViewBag.Error = "An error has occurred. Details: " + eee.Message;
return View();
}
}
It turns out that the integration is like this:
Question
How should I defensively code for the above situation? (or similar hybrid situations)?
Old question but it seems others have run into this.
This isn't really an MSAL issue since the token itself isn't the problem here. The exception stems from calling https://graph.microsoft.com/v1.0/me/messages
when the user doesn't have an accessible mailbox.
You generally can determine which resources have been assigned to a user
by looking at that user's provisionedPlans
collection:
https://graph.microsoft.com/v1.0/me?$select=provisionedPlans
This will return a collection resources that have been provisioned for the user. For example, this user does not have Exchange provisioned:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(provisionedPlans)/$entity",
"provisionedPlans": [
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "SharePoint"
},
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "SharePoint"
}
]
}
Compare this with this user that does have Excange provisioned:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(provisionedPlans)/$entity",
"provisionedPlans": [
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "MicrosoftOffice"
},
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "exchange"
},
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "MicrosoftCommunicationsOnline"
},
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "SharePoint"
},
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "SharePoint"
}
]
}
A similar property that may also be useful is assignedPlans
. This returns the resources that have been assigned to the user
and is useful for determining if provisioning simply hasn't occurred yet or for resources that are not formally "provisioned" such as Microsoft Teams.
That said, you should always defensively code for the possibility that a user doesn't have or lacks permission for a resource you're requesting. There are several scenarios where a resource may be inaccessible due to an outage or user permissions and the only sure fire way to handle these scenarios is to backstop with solid exception handling.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With