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

Stu*_*ish 5 c# azure-active-directory asp.net-core microsoft-graph-api

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

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

什么工作:

await graphClient.Me.SendMail(email, SaveToSentItems: false).Request().PostAsync();
Run Code Online (Sandbox Code Playgroud)

什么不起作用:

string FromUserEmail = "notifications@contoso.com";
await graphClient.Users[FromUserEmail].SendMail(email, SaveToSentItems: false).Request().PostAsync();
Run Code Online (Sandbox Code Playgroud)

还尝试直接使用用户对象 id:

await graphClient.Users["cd8cc59c-0815-46ed-aa45-4d46c8a89d72"].SendMail(email, SaveToSentItems: false).Request().PostAsync();
Run Code Online (Sandbox Code Playgroud)

我的应用程序有权将 Graph APISend mail as any user启用并由所有者/管理员授予“ ”。

API返回的错误信息:

代码:ErrorFolderNotFound 消息:在存储中找不到指定的文件夹。

我认为这个错误可能是因为通知帐户没有发送文件夹,所以我将该SaveToSentItems值设置为 false,但我仍然收到相同的错误。

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

我在这里查看了文档:https : //developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_sendmail

这似乎支持我正在尝试做的事情,但没有引用任何文件夹,除了我告诉 API 无论如何不要保存的已发送项目文件夹。

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

Jas*_*ton 5

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

如果用户尚未配置具有能够“发送为”notifications@contoso.com 用户的权限,则您将看到此错误。

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

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

  • 应用: Send mail as any user
  • 委托: Send mail on behalf of others

由于您使用的是委托,因此该权限不允许您以任何用户的身份发送,只能代表登录用户有权发送的任何人发送。

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

  • 你能指点我“以任何用户身份”发送邮件的文档吗?微软文档解释了如何以个人用户身份发送邮件,但我想在没有用户登录的情况下对 Graph 进行身份验证 - 用户本身将无权将邮件发送到我们想要使用的通讯组列表。 (2认同)

Mic*_*ael 5

所以像 Schwarzie2478 一样,我们使用了 noreply@ourcompany.com 地址。但是我们的 AD 是联合的,这意味着您不能使用 Username\Password auth,我们不想使用 Application Mail.Send 权限,因为它实际上可以作为任何人发送,而且 IT 安全部门不会放过它。所以我们改用了 Windows 身份验证。

这就要求你同意授予该应用通过将使用mail.send和user.read委派权限https://login.microsoftonline.com/ {} tenantId {/oauth2/v2.0/authorize?client_id=的clientId }&response_type=code&scope=user.read%20mail.send 并使用应用程序运行的 Windows 用户登录。

有关在此处使用 Windows 身份验证的更多信息:https : //github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Integrated-Windows-Authentication

// 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;
}
Run Code Online (Sandbox Code Playgroud)