I am working with Microsoft Graph and have created an app that reads mail from a specific user.
However, after getting an access token and trying to read the mailfolders, I receive a 401 Unauthorized answer. The detail message is:
The token contains no permissions, or permissions cannot be understood.
This seems a pretty clear message, but unfortunately I am unable to find a solution. This is what I have done so far:
Giving it application permissions Mail.Read, Mail.ReadWrite (https://learn.microsoft.com/en-us/graph/api/user-list-mailfolders?view=graph-rest-1.0)
Have gotten administrator consent.
The permissions are:
- Written the code below to acquire an access token:
// client_secret retrieved from secure storage (e.g. Key Vault) string tenant_id = "xxxx.onmicrosoft.com"; ConfidentialClientApplication client = new ConfidentialClientApplication( "..", $"https://login.microsoftonline.com/{tenant_id}/", "https://dummy.example.com", // Not used, can be any valid URI new ClientCredential(".."), null, // Not used for pure client credentials new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = client.AcquireTokenForClientAsync(scopes).Result;
string token = result.AccessToken;
So far so good. I do get a token.
Now I want to read the mail folders:
url = "https://graph.microsoft.com/v1.0/users/{username}/mailFolders"; handler = (HttpWebRequest)WebRequest.Create(url); handler.Method = "GET"; handler.ContentType = "application/json"; handler.Headers.Add("Authorization", "Bearer " + token); response = (HttpWebResponse)handler.GetResponse(); using (StreamReader sr = new StreamReader(response.GetResponseStream())) { returnValue = sr.ReadToEnd(); }
This time I receive a 401 message, with the details:
The token contains no permissions, or permissions cannot be understood.
I have searched the internet, but can’t find an answer to why my token has no permissions.
Thanks for your time!
update 1
If I use Graph Explorer to read the mailfolders, then it works fine. Furthermore: if I grap the token id from my browser en use it in my second piece of code, then I get a result as well. So, the problem is really the token I receive from the first step.
To ensure this works like you expect, you should explicitly state for which tenant you wish to obtain the access token. (In this tenant, the application should, of course, have already obtained admin consent.)
Instead of the "common" token endpoint, use a tenant-specific endpoint:
string url = "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token";
(Where {tenant-id}
is either the tenant ID of the tenant (a Guid), or any verified domain name.)
I would also strongly recommend against building the token request on your own, as you show in your question. This may be useful for educational purposes, but will tend to be insecure and error-prone in the long run.
There are various libraries you can use for this instead. Below, an example using the Microsoft Authentication Library (MSAL) for .NET:
// client_secret retrieved from secure storage (e.g. Key Vault)
string tenant_id = "contoso.onmicrosoft.com";
ConfidentialClientApplication client = new ConfidentialClientApplication(
client_id,
$"https://login.microsoftonline.com/{tenant_id}/",
"https://dummy.example.com", // Not used, can be any valid URI
new ClientCredential(client_secret),
null, // Not used for pure client credentials
new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = client.AcquireTokenForClientAsync(scopes).Result
string token = result.AccessToken;
// ... use token
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