Azure-Samples / Azure-Samples/ms-identity-docs-code-dotnet

Daemon Sample always returns Unauthorized although correct permissions are assigned

Open
#103 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
65
Forks
52
PR merge metrics
No merged PRs in 30d

Description

> Please provide us with the following information:
> ---------------------------------------------------------------

### This issue is for a: (mark with an `x`)
```
- [x] bug report -> please search issues before submitting
- [ ] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)
```

### Minimal steps to reproduce
Run the attached code. The response from httpClient.SendAsync(request) always returns Unauthorized

### Any log messages given by the failure
> None

### Expected/desired behavior
> Authorized!

### OS and Version?
> Windows 11 Home

### Versions
>10.0.26100

### Mention any other details that might be useful

Unauthorized error:
{"error":{"code":"OrganizationFromTenantGuidNotFound","message":"The tenant for tenant guid '0ac2da02-f40e-4889-977c-67abc6b1ea17' does not exist.","innerError":{"oAuthEventOperationId":"be6f1ba5-ec8f-4bc1-8886-79677c609d98","oAuthEventcV":"dG+k3EExfgbY2xaPnR0BKA.1.1","errorUrl":"https://aka.ms/autherrors#error-InvalidTenant","requestId":"6646c447-ce81-4d17-b16a-38069494e270","date":"2025-01-11T23:47:34"}}}

The tenant ID is correctly set. I promise

> I've correctly entered all GUIDs and verified 9 times. Authenticates fine. I have all mail permissions set to application scope and assigned by admin.

```
using Microsoft.Identity.Client;
using System.Net.Http.Headers;
using System.Text.Encodings.Web;
using System.Text.Json;

// based on https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-daemon-dotnet-acquire-token

// entra Tenant ID = 0ac2da02-f40e-4889-977c-67abc6b1ea17
// az portal Tenant ID = 0ac2da02-f40e-4889-977c-67abc6b1ea17
var tenantId = "00000000-f40e-4889-977c-67abc6b1ea17";

// entra Client ID = 0c29ca3a-0bcf-4861-8430-e5f47abf0972
// az portal Client ID = 0c29ca3a-0bcf-4861-8430-e5f47abf0972
var clientId = "11111111-0bcf-4861-8430-e5f47abf0972";

var clientSecret = "secret"; // the client secret obtained from the Microsoft Entra admin center",

// entra Client Object ID = 8e7c972c-289c-4fe2-b53e-a8a3dbe797cf (service principal ID - enterprise app)
// az portal Client Object ID = 088843fe-1ace-40d2-9c26-3f9a5a358a03
// not sure why I have two different Object ID's for the application but the azure portal one authenticates and the entra one does not
var clientObjectId = "22222222-1ace-40d2-9c26-3f9a5a358a03";

// entra User Object ID = 17142bbb-92cf-4c7d-b91b-d017534463d1
// az portal User Object ID = 17142bbb-92cf-4c7d-b91b-d017534463d1
var userId = "33333333-92cf-4c7d-b91b-d017534463d1";

var authority = $"https://login.microsoftonline.com/{tenantId}";

/**
* In Azure app permissions - these and many more:
* Mail.Read = Both Application and Delegated
* User.Read = Both Application and Delegated
* Mail.ReadWrite = Both Application and Delegated
* MailboxFolder.Read = Both Application and Delegated
* MailboxSettings.Read = Both Application and Delegated
* Mail.Read.Shared = Delegated
* Mail.Send = Both Application and Delegated
*/

// This app instance should be a long-lived instance because
// it maintains the in-memory token cache.
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();

// Acquire token for client credentials flow
var authenticationResult = await app.AcquireTokenForClient(["https://graph.microsoft.com/.default"]).ExecuteAsync();
var bearerToken = authenticationResult.AccessToken;

var httpClient = new HttpClient();
await Authenticate(httpClient, clientObjectId, bearerToken);

// At this point I have successful authentication

await GetMailboxesAsync(httpClient, bearerToken, clientObjectId); // <== this is where I get unauthorized

await SendEmailAsync(httpClient, userId, bearerToken); // <== this is where I get unauthorized
Console.ReadLine();

static async Task Authenticate(HttpClient httpClient, string clientObjectId, string bearerToken)
{
using var graphRequest = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/applications/{clientObjectId}");
graphRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
graphRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var graphResponseMessage = await httpClient.SendAsync(graphRequest);

if (graphResponseMessage.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
Console.WriteLine("Access denied. Please check the permissions granted to the application.");
return;
}

using var graphResponseJson = JsonDocument.Parse(await graphResponseMessage.Content.ReadAsStreamAsync());
Console.WriteLine(JsonSerializer.Serialize(graphResponseJson,
new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }));
}

static async Task GetMailboxesAsync(HttpClient httpClient, string token, string userId)
{
// Call the GetMailboxes endpoint to get a list of mailboxes.
var getMailboxesRequest = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/users/{userId}/mailFolders");
getMailboxesRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var getMailboxesResponse = await httpClient.SendAsync(getMailboxesRequest);
if (getMailboxesResponse.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Console.WriteLine($"Access {getMailboxesResponse.StatusCode}. Why is this unauthorized?"); // <== this is where I get unauthorized
return;
}
if (!getMailboxesResponse.IsSuccessStatusCode)
{
Console.WriteLine($"Access {getMailboxesResponse.StatusCode}. ??");
return;
}

getMailboxesResponse.EnsureSuccessStatusCode();

// Parse the response to get a list of mailboxes.
var mailboxesJson = await getMailboxesResponse.Content.ReadAsStringAsync();
var mailboxes = JsonDocument.Parse(mailboxesJson).RootElement;
foreach (var mailbox in mailboxes.EnumerateArray())
{
Console.WriteLine(mailbox.GetProperty("displayName").GetString());
}

}
static async Task SendEmailAsync(HttpClient httpClient, string userId, string token)
{
var emailMessage = new
{
message = new
{
subject = "Test Email",
body = new
{
contentType = "Text",
content = "Hello, this is a test email!"
},
toRecipients = new[]
{
new
{
emailAddress = new
{
address = "wes.smith@outlook.com"
}
}
}
},
saveToSentItems = "true"
};

var requestContent = new StringContent(JsonSerializer.Serialize(emailMessage));
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var request = new HttpRequestMessage(HttpMethod.Post, $"https://graph.microsoft.com/v1.0/users/{userId}/sendMail")
{ Content = requestContent };
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.SendAsync(request);

if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Console.WriteLine($"Access {response.StatusCode}. Why is this unauthorized?"); // <== this is where I get unauthorized
return;
}
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"Access {response.StatusCode}. ??");
return;
}

Console.WriteLine(response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.