C# Microsoft Graph API-以其他用户身份发送电子邮件

C# Microsoft Graph API-以其他用户身份发送电子邮件,c#,asp.net-core,azure-active-directory,microsoft-graph-api,C#,Asp.net Core,Azure Active Directory,Microsoft Graph Api,在我们的应用程序中,我们需要通过电子邮件向用户发送各种事件触发器的通知 如果我以“我”的身份发送当前用户,我可以发送电子邮件,但尝试以另一个用户帐户的身份发送会返回错误消息,如果通知不是来自用户自己,并且可能包含我们不希望在已发送文件夹中浮动的信息,我更希望这样做 工作原理: await graphClient.Me.SendMail(email, SaveToSentItems: false).Request().PostAsync(); 什么不起作用: string FromUserEma

在我们的应用程序中,我们需要通过电子邮件向用户发送各种事件触发器的通知

如果我以“我”的身份发送当前用户,我可以发送电子邮件,但尝试以另一个用户帐户的身份发送会返回错误消息,如果通知不是来自用户自己,并且可能包含我们不希望在已发送文件夹中浮动的信息,我更希望这样做

工作原理:

await graphClient.Me.SendMail(email, SaveToSentItems: false).Request().PostAsync();
什么不起作用:

string FromUserEmail = "notifications@contoso.com";
await graphClient.Users[FromUserEmail].SendMail(email, SaveToSentItems: false).Request().PostAsync();
还尝试直接使用用户对象id:

await graphClient.Users["cd8cc59c-0815-46ed-aa45-4d46c8a89d72"].SendMail(email, SaveToSentItems: false).Request().PostAsync();
我的应用程序拥有由所有者/管理员启用和授予的Graph API“
以任何用户身份发送邮件”的权限

API返回的错误消息:

代码:ErrorFolderNotFound消息:无法找到指定的文件夹 在商店里找到的

我认为此错误可能是因为通知帐户没有已发送文件夹,因此我将
SaveToSentItems
值设置为false,但仍然会出现相同的错误

我是否需要检查帐户本身的任何设置,以允许应用程序在此帐户上发送邮件,或者这是否可行

我已在此处查阅了文档:

它似乎支持我试图做的事情,但除了我告诉API无论如何不要保存到的sent items文件夹之外,它不引用任何文件夹


我们不打算在这里冒充任何实际用户,只需从这个特定帐户的应用程序内部发送通知电子邮件(我知道这在技术上是冒充,但不是真正的实体)。

我不知道其他人会为此做些什么,但我联系了微软,询问了这个确切的情况:我想以固定用户身份发送邮件(noreply@mycompany.com)它在Azure中有一个邮箱。我想从不同的应用程序或服务发送此邮件

那里的人告诉我,只有使用委托用户令牌才能在没有用户登录的情况下发送邮件

因此,我们将应用程序配置为Azure中的本地应用程序,类似于移动应用程序。在安装阶段,通过技术用户登录此应用程序,可以为该特定用户提供一个可存储在邮件服务或组件中的委托用户令牌。此令牌不会过期(至少在用户的安全性(如密码或其他)发生更改之前不会过期),并且在您授予此帐户发送邮件的权限时,可以使用此令牌调用graph api来发送邮件

其次,我们甚至将其他共享邮箱与此帐户关联,以便能够为这些邮箱发送邮件

文件:

首先,您需要在Azure中注册本机应用程序(不是Web API):

此应用程序只需要用户的一次性登录和批准,即可获得可以不确定地表示该用户的令牌。我们为此设置了一个邮件用户帐户。然后,该令牌用于获取对Graph Api的访问令牌,以便发送邮件等

令牌处理示例:

使用存储的identitytoken(通常是某处的.cache文件),您可以请求accesstoken:

身份客户端:

authResult=wait_clientApp.AcquireTokenSilentAsync(作用域

        private static string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";

    //Set the scope for API call to user.read
    private static string[] scopes = new string[] { "user.read", "mail.send" };
    private const string GraphApi = "https://graph.microsoft.com/";

            var graphclient = new GraphServiceClient($"{GraphApi}/beta",
                       new DelegateAuthenticationProvider(
                           (requestMessage) =>
                           {                                  
                                   // inject bearer token for auth
                                   requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
                               return Task.FromResult(0);
                           }));

        var sendmail = graphclient.Users[User].SendMail(mail), true);
        try
        {
            await sendmail.Request().PostAsync();
        }
        catch (Exception e)
        {

无论何时使用委派权限(即用户登录时),即使管理员已同意Mail.Send.Shared,也不会授予对租户中所有邮箱的访问权限。这些OAuth权限不会覆盖用户的权限(和限制)

如果用户尚未配置权限,无法“作为”发送notifications@contoso.com用户,然后您将看到此错误

要使其正常工作,您需要向将使用您的应用程序的所有用户授予“发送为”权限

这是一件微妙的事情,当然有点令人困惑。在Azure门户中,权限的描述略有不同,这取决于您是查看应用程序权限还是委托权限

  • 应用程序:
    以任何用户的身份发送邮件
  • 委派:
    代表他人发送邮件
由于您使用的是委派,因此该权限不允许您以任何用户的身份发送,只能代表登录用户有权以其身份发送的任何人员发送


您可以在此处使用另一种方法来避免将这些权限授予所有用户(这将允许他们通过Outlook等进行发送)将是让您的后端应用程序使用客户端凭据流来获取仅应用程序令牌。在这种情况下,应用程序本身将具有作为任何用户发送的权限。

因此,就像Schwarzie2478一样,我们使用了noreply@ourcompany.com但我们的广告是联合的,这意味着你不能使用用户名\密码身份验证,我们不想使用应用程序电子邮件。发送权限,因为它实际上可以作为任何人发送,而且it安全部门不可能允许它这样做。所以我们改用Windows身份验证

这要求您通过转到{tenantId}/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read%20mail.send并以应用程序将作为运行的windows用户登录,授予应用程序使用mail.send和user.read委托权限的许可

有关使用windows身份验证的详细信息,请单击此处:

//方法调用
var t=sendmailusinggraphapi();
t、 等待();
//方法
静态异步任务SendEmailUsingGraphAPI(){
//认证
var tenantID=“您的\u租户\u ID”;//azure ad租户/目录ID
var clientID=“您的应用程序客户端ID”;//已注册的应用程序客户端ID
var scopes=“user.read mail.send”;//委托请求所需的权限
字符串权限=$”https://login.microsoftonline.com/{tenantID}”;
string[]scopesArr=新字符串[]{scopes};
试一试{
IPPublicClientApplicationApp=PublicClientApplicationBuilder
.创建(客户端)
        private static string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";

    //Set the scope for API call to user.read
    private static string[] scopes = new string[] { "user.read", "mail.send" };
    private const string GraphApi = "https://graph.microsoft.com/";

            var graphclient = new GraphServiceClient($"{GraphApi}/beta",
                       new DelegateAuthenticationProvider(
                           (requestMessage) =>
                           {                                  
                                   // inject bearer token for auth
                                   requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
                               return Task.FromResult(0);
                           }));

        var sendmail = graphclient.Users[User].SendMail(mail), true);
        try
        {
            await sendmail.Request().PostAsync();
        }
        catch (Exception e)
        {
// method call
var t = SendEmailUsingGraphAPI();
t.Wait();

// method
static async Task<Boolean> SendEmailUsingGraphAPI() {

    // AUTHENTICATION
    var tenantID = "YOUR_TENANT_ID"; //azure ad tenant/directory id
    var clientID = "YOUR_APPS_CLIENT_ID"; // registered app clientID
    var scopes = "user.read mail.send";  // DELEGATE permissions that the request will need

    string authority = $"https://login.microsoftonline.com/{tenantID}";
    string[] scopesArr = new string[] { scopes };

    try {
        IPublicClientApplication app = PublicClientApplicationBuilder
                .Create(clientID)
                .WithAuthority(authority)
                .Build();

        var accounts = await app.GetAccountsAsync();

        AuthenticationResult result = null;
        if (accounts.Any()) {
            result = await app.AcquireTokenSilent(scopesArr, accounts.FirstOrDefault())
                .ExecuteAsync();
        }
        else {
            // you could acquire a token by username/password authentication if you aren't federated.
            result = await app.AcquireTokenByIntegratedWindowsAuth(scopesArr)
                //.WithUsername(fromAddress)
                .ExecuteAsync(CancellationToken.None);
        }

        Console.WriteLine(result.Account.Username);


        // SEND EMAIL
        var toAddress = "EMAIL_OF_RECIPIENT";
        var message = "{'message': {'subject': 'Hello from Microsoft Graph API', 'body': {'contentType': 'Text', 'content': 'Hello, World!'}, 'toRecipients': [{'emailAddress': {'address': '" + result.Account.Username + "'} } ]}}";

        var restClient = new RestClient("https://graph.microsoft.com/v1.0/users/" + result.Account.Username + "/sendMail");
        var request = new RestRequest(Method.POST);
        request.AddHeader("Content-Type", "application/json");

        request.AddHeader("Authorization", "Bearer " + result.AccessToken);
        request.AddParameter("", message, ParameterType.RequestBody);
        IRestResponse response = restClient.Execute(request);
        Console.WriteLine(response.Content);

    }
    catch (Exception e) {
        Console.WriteLine(e.Message);
        throw e;
    }

    return true;
}