Mail.Send restricted in C# con Microsoft Graph API
In questo post viene spiegato come è possibile inviare una mail tramite Graph API di Azure restringendo la possibilità di usare solo uno specifico mittente.
Questa possibilità torna utile per evitare che una applicazione possa inviare mail impersonando qualunque utente del tenant.
Cosa serve:
A questa app va assegnato il permesso Mail.Send di tipo Application
Si può usare un utente esistente o crearne uno nuovo, dedicato all'applicazione, con relativa mailbox (ad esempio usersend@xxxx.onmicrosoft.com).
Per eseguire questi comandi è necessario installarli
Successivamente va creata la policy che restringe l'accesso solo ad una specifica mailbox
Per la demo sarà necessario installare questi 2 pacchetti NuGet
e il codice di esempio è questo
Questa possibilità torna utile per evitare che una applicazione possa inviare mail impersonando qualunque utente del tenant.
Cosa serve:
- Un App Registration
- Un utente con mailbox da usare come mittente
- Una policy su Exchange online
- il codice C# di invio
App Registration
Per poter inviare una mail tramite le Graph API è necessario creare un App Registration su Azure Active Directory creando un secret che servirà successivamente (oltre al tenanId e al clientId).A questa app va assegnato il permesso Mail.Send di tipo Application
Il permesso va confermato, da un amministratore, premendo su Grant admin consent for ...
Mailbox
Per l'invio è necessario usare la mail box di un utente.Si può usare un utente esistente o crearne uno nuovo, dedicato all'applicazione, con relativa mailbox (ad esempio usersend@xxxx.onmicrosoft.com).
Exchange online
Tramite i comandi PowerShell di Exchange online va creata la policy che vincola la App Registration ad usare solo una mailbox come mittente.Per eseguire questi comandi è necessario installarli
PowerShell
# installa il modulo ExchangeOnlineManagement
Install-Module -Name ExchangeOnlineManagement
# lo importa nella console PowerShell
Import-Module ExchangeOnlineManagement
# imposta la policy di esecuzione degli script
Set-ExecutionPolicy RemoteSigned
Eseguire questi comandi in elevati privilegi
Successivamente va creata la policy che restringe l'accesso solo ad una specifica mailbox
PowerShell
# l'utente admin su Exchange online con cui fare login
$loginUser = "useradmin@xxxx.onmicrosoft.com"
# il clientID dell'App Registration
$clientId = "17430451-xxxx-xxxx-xxxx-f256ab48cbf3"
# il nome con cui verrà creato il distribution group in cui verrà inserito il mittente
$distributionGroup = "TestSendMailRestrictedSgartIt"
# l'unico mittente con cui sarà possibile inviare le mail
$fromAddress = "usersend@xxxx.onmicrosoft.com"
# connessione con Exchange online
Connect-ExchangeOnline -UserPrincipalName $loginUser
# creazione di un Distribution Group
$ds = New-DistributionGroup -Name $distributionGroup -Alias $distributionGroup.toLower() -Type security
$primarySmtpAddress = $ds.PrimarySmtpAddress
# creo una nuova policy di tipo "RestrictAccess" e la associo alla App Registration e al Distribution Group
$aap = New-ApplicationAccessPolicy -AppId $clientId -PolicyScopeGroupId $primarySmtpAddress -AccessRight RestrictAccess -Description "Restrict this app to members of distribution group $distributionGroup"
# aggiungo al Distribution Group la mailbox da usare come mittente
Add-DistributionGroupMember -Identity $distributionGroup -Member $fromAddress -Confirm:$false
# visualizzo l'appartenenza al gruppo
Get-DistributionGroupMember $distributionGroup
Codice di esempio
Adesso ci sono tutti gli elementi per realizzare un esempio di invio mail in C#.Per la demo sarà necessario installare questi 2 pacchetti NuGet
XML: Nuget
<PackageReference Include="Azure.Identity" Version="1.9.0" />
<PackageReference Include="Microsoft.Graph" Version="5.14.0" />
C#: Invio mail in .NET 6 con Graph API restricted
using Azure.Identity;
using Microsoft.Graph.Models;
using Microsoft.Graph;
using Microsoft.Graph.Users.Item.SendMail;
TestSendMail().Wait();
static async Task TestSendMail()
{
string tenantId = "b32d8...2dca9";
string clientId = "17430....bf3";
string clientSecret = "yM_....or";
// questo è l'unico mittente accettato
string fromAddress = "usersend@xxxx.onmicrosoft.com";
// destinatario
string toAddress = "destinatario@dominio.it";
string subject = "Prova invio mail restricted";
string body = "<b>invio riuscito</b>";
try
{
// connessione alle Graph API
ClientSecretCredential credential = new(tenantId, clientId, clientSecret);
using GraphServiceClient graphClient = new(credential);
// Costruisco il messaggio da inviare
// di tipo Microsoft.Graph.Users.Item.SendMail.SendMailPostRequestBody
SendMailPostRequestBody postRequest = new()
{
SaveToSentItems = true,
Message = new()
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = body
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = toAddress
}
}
}
}
};
// Invio il messaggio
await graphClient
.Users[fromAddress]
.SendMail
.PostAsync(postRequest);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
Da notare il parametro SaveToSentItems che permette di scegliere se conservare una copia del messaggio nella mailbox.
Per verificare la restrizione sul mittente e sufficiente cambiare la variabile fromAddress con un altra mail del tenant, si otterrà un errore di accessoException of type 'Microsoft.Graph.Models.ODataErrors.ODataError' was thrown