0

I am trying to send email using Azure Communication Service and DefaultAzureCredential from my local machine but I am getting the following error:

Azure.Identity.AuthenticationFailedException: Azure CLI authentication failed due to an unknown error. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/azclicredential/troubleshoot 
ERROR: AADSTS65002: Consent between first party application '04b07795-8ddb-461a-bbee-02f9e1bf7b46' 
and first party resource '632ec9eb-fad7-4cbd-993a-e72973ba2acc' must be configured via preauthorization - applications owned and operated by Microsoft must get approval from the API owner before requesting tokens for that API.

Here's the code that is failing:

using Azure;
using Azure.Communication.Email;
using Azure.Identity;

var credentials = new DefaultAzureCredential(new DefaultAzureCredentialOptions() { TenantId = "my-tenant-id" });
client = new EmailClient(new Uri("https://mydomain.communication.azure.com/"), credentials);
var subject = "Welcome to Azure Communication Service Email APIs.";
var htmlContent = "<html><body><h1>Quick send email test</h1><br/><h4>This email message is sent from Azure Communication Service Email.</h4><p>This mail was sent using .NET SDK!!</p></body></html>";
var sender = "DoNotReply@mydomain.com";
var recipient = "user@mydomain.com";

var message = new EmailMessage(sender, recipient, new EmailContent(subject) { Html = htmlContent });
var operation = await client.SendAsync(WaitUntil.Started, message);

The same code works if I use a Service Principal. Here's the code that is working:

using Azure;
using Azure.Communication.Email;
using Azure.Identity;

var credentials = new ClientSecretCredential("tenant-id",
    "client-id", "client-secret");;
client = new EmailClient(new Uri("https://mydomain.communication.azure.com/"), credentials);
var subject = "Welcome to Azure Communication Service Email APIs.";
var htmlContent = "<html><body><h1>Quick send email test</h1><br/><h4>This email message is sent from Azure Communication Service Email.</h4><p>This mail was sent using .NET SDK!!</p></body></html>";
var sender = "DoNotReply@mydomain.com";
var recipient = "user@mydomain.com";

var message = new EmailMessage(sender, recipient, new EmailContent(subject) { Html = htmlContent });
var operation = await client.SendAsync(WaitUntil.Started, message);

Accepted answer provided Azure Communication Services - How do I authenticate against Azure IAM suggests that I use a Service Principal and that works perfectly fine however I do not want to use a Service Principal.

Other answer provided in the same question mentions that the user should be in Contributor role and the logged-in user does have that role.

enter image description here

Is there a way to send email from local machine using the credentials of a logged in user and not a Service Principal?

Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • Currently, the recommendation is to store the service principle information in Environment variables and not in the code. [Use Azure Active Directory in Communication Services](https://learn.microsoft.com/azure/communication-services/quickstarts/identity/service-principal?pivots=platform-azcli). I’m able to repro the issue, it's most likely being caused by a limitation in from our resource provider not supporting that scenario. I have relayed the feedback to our ACS product engineering team, as soon as we have more updates, I will share it here. – AjayKumar May 09 '23 at 18:44
  • Thanks Ajay. Please post your comment as an answer and I’ll mark as accepted. – Gaurav Mantri May 10 '23 at 00:40
  • Gaurav, Glad you found the info helpful. I have converted/posted my comment as an answer. Thanks! – AjayKumar May 12 '23 at 20:10

3 Answers3

1

Converting to answer:

Currently, the recommendation is to store the service principle information in Environment variables and not in the code. Use Azure Active Directory in Communication Services. I’m able to repro the issue, it's most likely being caused by a limitation in from our resource provider not supporting that scenario. I have relayed the feedback to our ACS product engineering team, as soon as we have more updates, I will share it here.

AjayKumar
  • 2,812
  • 1
  • 9
  • 28
1

I was able to use the code below to send an email using just my logged-in credentials. I confirmed that I did not have any environment variables that could have given a false result. Initially, there was an issue in the backend configuration, but that has been resolved.

using Azure;
using Azure.Communication.Email;
using Azure.Identity;

namespace SendEmail
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            // This code demonstrates how to authenticate to your Communication Service resource using
            string resourceEndpoint = "https://<redacted>.communication.azure.com";
            EmailClient emailClient = new EmailClient(new Uri(resourceEndpoint), new DefaultAzureCredential());

            //Replace with your domain and modify the content, recipient details as required

            var subject = "Welcome to Azure Communication Service Email APIs.";
            var htmlContent = "<html><body><h1>Quick send email test</h1><br/><h4>This email message is sent from Azure Communication Service Email.</h4><p>This mail was sent using .NET SDK!!</p></body></html>";
            var sender = "DoNotReply@<redacted>.azurecomm.net";
            var recipient = "<redacted>@microsoft.com";

            try
            {
                Console.WriteLine("Sending email...");
                EmailSendOperation emailSendOperation = await emailClient.SendAsync(
                    Azure.WaitUntil.Completed,
                    sender,
                    recipient,
                    subject,
                    htmlContent);
                EmailSendResult statusMonitor = emailSendOperation.Value;

                Console.WriteLine($"Email Sent. Status = {emailSendOperation.Value.Status}");

                /// Get the OperationId so that it can be used for tracking the message for troubleshooting
                string operationId = emailSendOperation.Id;
                Console.WriteLine($"Email operation id = {operationId}");
            }
            catch (RequestFailedException ex)
            {
                /// OperationID is contained in the exception message and can be used for troubleshooting purposes
                Console.WriteLine($"Email send operation failed with error code: {ex.ErrorCode}, message: {ex.Message}");
            }
        }
    }
}

I used az login from Windows PowerShell to login.

Here's the output when I use a login with access to the resource: Console output of a successful email send request

Here's the output when I use a login without access to the resource: Console output of an unsuccessful email send request

Use the following command from the command line to confirm that you are able to receive an access token. Before the update, this did not work.

az account get-access-token --resource https://communication.azure.com

I will check this post again in case there's another related issue that is causing your problem.

ddouglas
  • 11
  • 1
0

I got no issues when using my user logged account or service principal to send ACS Email (or use any other Azure Services). The error message could mean you have to login using Azure Command-Line Interface (CLI) first, the login will be interaction login with consent.

Using DefaultAzureCredential class

The DefaultAzureCredential class should be enough for all your needs.

Reference: https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential

I saw 2 cases in your code, DefaultAzureCredential can be used for both your 2 cases. And should not put any sensitive information in the source code.

1. If you want to use your user logged in:

var credential = new DefaultAzureCredential();
// or new DefaultAzureCredential(includeInteractiveCredentials: false);

You have to login using your user first, run the az login and complete the interaction sign-in before run the .NET Application.

https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli

If you have multiple Azure Subsciptions, your may need to switch to the subscription first (the subscription of the Azure resource you want to test sending email)

az account show  # check the current active subscription

az account list  # list all subscriptions

az account set --subscription "3b558d3a-.........-465701d89f9d"

2. If you want to use service principal

var credential = new DefaultAzureCredential();
// or new DefaultAzureCredential(includeInteractiveCredentials: false);
// same C# code as case using your user logged in

Setting up environment on your developer computer: you can set AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET on Windows Environment Variable settings

enter image description here

You also can set the azure credential to the Environment in launchSettings.json on Visual Studio (dotnet run also use launchSettings.json)

Remember not commit this launchSettings.json with service principal credential to git repository

enter image description here

enter image description here

3. On Azure, you also can use DefaultAzureCredential for Managed identity, which much more secure than service principal.

  • If you use System-Assigned Managed identity for your application on Azure, donot provide any of AZURE_CLIENT_ID

  • If you use User-Assigned Managed identity, make sure you setup AZURE_CLIENT_ID of the User-Assigned Managed identity

Using Azure credential for ACS Email, instead of ACS Email AzureKeyCredential or connection string

  • In code, use DefaultAzureCredential as (1). For your user logged in, make sure run az login first, or run az account show to see if you already logged in.

  • Grant your user account (or Service principal, or managed identity) to role Contributor on the ACS (Access control (IAM)).

Then it is enough to send email on ACS Email.

Tho Ho
  • 687
  • 10
  • 13
  • Thank you for providing detailed answer. Please try by removing the environment variables for tenant id, client id and client secret and then use DefaultAzureCredential(). I did everything you mentioned in your answer but was still getting the error. – Gaurav Mantri May 15 '23 at 11:13
  • Have you run on CMD Command line: az login? – Tho Ho May 15 '23 at 11:27
  • Yes, I did that. Everywhere in my code DefaultAzureCredential works except here. – Gaurav Mantri May 15 '23 at 12:02
  • Hi @GauravMantri as @ddouglas answer, he also can use the logged account to send email, he just need to run `az login` (he run `az login` on Powershell instead CMD, but should same result with running `az login` on CMD). Then can try to check if your account have permission on ACS by run `az account get-access-token --resource https://communication.azure.com`. I take the accessToken and verify it on https://jwt.io, I can see that my account information there in the JWT. I also tried the sample code from the quickstarts to send email, then got similar result to @ddouglas trying. – Tho Ho May 17 '23 at 08:21