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:
- 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
App RegistrationIl 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# 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
# 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<PackageReference Include="Azure.Identity" Version="1.9.0" />
<PackageReference Include="Microsoft.Graph" Version="5.14.0" />
e il codice di esempio è questo
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 accesso
Exception of type 'Microsoft.Graph.Models.ODataErrors.ODataError' was thrown
Exception