代码之家  ›  专栏  ›  技术社区  ›  nadine

OAuth2通过身份验证的电子邮件从PowerShell发送到Exchange Online

  •  0
  • nadine  · 技术社区  · 2 年前

    第100次OAuth2,对不起,但我在这里濒临绝望。我需要将经过身份验证的电子邮件从PowerShell脚本发送到我自己的Exchange Online邮箱(如果这很重要的话,我也是租户管理员)。所以我在AzureAD注册了一个应用程序。

    显示名称: MyApp
    支持的帐户类型: My organization only
    应用程序权限:
    Microsoft Graph \用户。读取(委派,授予MyDomain)
    Office 365 Exchange Online\IMAP。AccessAsApp(应用程序,授予MyDomain)

    可能是 Microsoft Graph 此处不需要API。该应用程序有一个秘密,有效期为730天。

    我创建了新的Exchange服务主体,并授予了对邮箱的完全访问权限:

    $app = Get-AzureADServicePrincipal -SearchString MyApp
    New-ServicePrincipal -AppId $app.AppId -ObjectId $app.ObjectId -DisplayName "MyServicePrincipalName"
    Add-MailboxPermission -Identity $azureUserName -User $app.ObjectId -AccessRights FullAccess
    

    以下是的结果 Get-MailboxPermission -Identity $azureUserName | fl :

    IsOwner         : False
    AccessRights    : {FullAccess, ReadPermission}
    Deny            : False
    InheritanceType : All
    User            : NT AUTHORITY\SELF
    UserSid         : S-X-Y-Z
    Identity        : admin
    IsInherited     : False
    IsValid         : True
    ObjectState     : Unchanged
    
    IsOwner         : False
    AccessRights    : {FullAccess}
    Deny            : False
    InheritanceType : All
    User            : MyServicePrincipalName
    UserSid         : S-X-Y-Z-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxx
    Identity        : admin
    IsInherited     : False
    IsValid         : True
    ObjectState     : Unchanged
    

    配置此选项后,我尝试将电子邮件添加到收件箱。这是我使用的脚本:

    Import-Module AzureAD
    Import-Module ExchangeOnlineManagement
    
    $tenantID = "asdf1234"
    $appID = "asdf1234"
    $secretValue = "asdf1234"
    $userName = "[email protected]"
    $senderAddress = $userName
    $recipientAddress = $userName
    
    $scope = "https://outlook.office365.com/.default"
    $tokenURL = "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token"
    
    # Define the OAuth 2.0 token request body
    $tokenRequestBody = @{
        client_id     = $appID
        scope         = $scope
        client_secret = $secretValue
        grant_type    = "client_credentials"
    } 
    
    # Request an access token
    $response = Invoke-RestMethod -Uri $tokenURL -Method POST -ContentType "application/x-www-form-urlencoded" -Body $tokenRequestBody
    
    # Access the access token
    $accessToken = $response.access_token
    
    # Connect to Office 365 IMAP service
    $server = "outlook.office365.com"
    $port = 993
    
    $tcpClient = [System.Net.Sockets.TcpClient]::new($server, $port)
    $sslStream = [System.Net.Security.SslStream]::new($tcpClient.GetStream())
    $sslStream.AuthenticateAsClient($server, $null, [System.Security.Authentication.SslProtocols]::Tls12, $false)
    $reader = [System.IO.StreamReader]::new($sslStream)
    $writer = [System.IO.StreamWriter]::new($sslStream)
    
    # Read server response
    $reader.ReadLine()
    
    # Compose the email
    $emailSubject = "Test e-mail"
    $emailBody = "This is a test e-mail"
    $email = "To: $recipientAddress`nFrom: $senderAddress`nSubject: $emailSubject`n`n$emailBody"
    
    # build XOAUTH2 login string with accesstoken and username
    $accessString ="user=" + $userName + "$([char]0x01)auth=Bearer " + $accessToken + "$([char]0x01)$([char]0x01)"
    $Bytes = [System.Text.Encoding]::ASCII.GetBytes($accessString)
    $loginString = [Convert]::ToBase64String($Bytes)
    
    # Authenticate using XOAUTH2
    $command = "A01 AUTHENTICATE XOAUTH2 $loginString"
    $writer.WriteLine($command)
    
    # Capture the response
    $ResponseStr = $reader.ReadLine()
    
    # Check if the response indicates success
    if ($ResponseStr -like "*OK AUTHENTICATE completed.") {
        Write-Host "Authentication successful."
    } else {
        Write-Host "Authentication failed: $ResponseStr"
    }
    
    # Send an email
    $command = "A01 APPEND INBOX (\Seen) $(" + $email.Length + ")"
    $writer.WriteLine($command)
    $writer.WriteLine($email)
    $writer.WriteLine()
    
    # Logout and clean up sessions
    $writer.WriteLine("A01 Logout")
    $writer.Close()
    $reader.Close()
    $sslStream.Close()
    $tcpClient.Close()
    

    我得到一个访问令牌 $response 看起来是这样的:

    标记类型 expires_in ext_expires_in access_token
    持票人 3599 3599 myaccesstokenstring

    但在我执行之后 $writer.WriteLine($command) ,它挂起,几秒钟后我得到错误:

    Authentication failed: * BYE Connection is closed. 13
    

    我真的不明白,我做错了什么。也许这对那些知道自己在做什么的人来说是完全愚蠢的。我只是想给自己发送电子邮件通知,我对这里的整个概念的理解非常基本。

    非常感谢您的帮助!

    1 回复  |  直到 2 年前
        1
  •  2
  •   Sridevi    2 年前

    或者,您可以使用 Microsoft Graph API 发送 来自PowerShell的经过身份验证的电子邮件。

    我注册了一个Azure AD应用程序并授予 Mail.Send 申请权限如下:

    enter image description here

    现在,我运行以下修改后的脚本 发送邮件 通过安装 Microsoft.Graph 模块如下:

    #Install-Module -Name Microsoft.Graph -Scope CurrentUser
    #Import-Module Microsoft.Graph.Users.Actions
    
    $tenantID = "tenantID"
    $appID = "appID"
    $secretValue = "secret"
    $ClientSecretPass = ConvertTo-SecureString -String $secretValue -AsPlainText -Force
    $ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $appID, $ClientSecretPass
    
    # Connect to Microsoft Graph with Client Secret
    Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential
    
    $userName = "[email protected]"
    $senderAddress = $userName
    $recipientAddress = $userName
    
    $emailSubject = "Test e-mail"
    $emailBody = "This is a test e-mail"
    $type = "Text" 
    
    $params = @{
        Message         = @{
            Subject       = $emailSubject
            Body          = @{
                ContentType = $type
                Content     = $emailBody
            }
            ToRecipients  = @(
                @{
                    EmailAddress = @{
                        Address = $recipientAddress
                    }
                }
            )
        }
    }
    
    Send-MgUserMail -UserId $senderAddress -BodyParameter $params
    

    回复:

    enter image description here

    当我办理入住手续时 Inbox 用户的文件夹,邮件添加成功,如下所示:

    enter image description here

    请注意,Graph API是 取代Exchange Online API。但您可以通过Graph API访问多个Microsoft 365服务,包括Exchange Online、SharePoint、OneDrive、Teams等。

    随着微软将其API整合到 Microsoft Graph API ,Exchange Online API可能最终会被弃用,取而代之的是Graph API。因此,建议使用Graph API进行新的开发,以简化复杂性。

    推荐文章