Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Gmail API to send an email using Invoke-WebRequest in Powershell

$firstEmail = "[email protected]";
$secondEmail = "[email protected]";

Function Set-MIMEBase64Encoded
{
Param(
[string]$subject
)

#Creates a MIME formatted email.
$text = "From: $firstEmail\r\n" + "To: $secondEmail\r\n" + "Subject: $subject\r\n\r\n" + "$subject";
$bytes = [System.Text.Encoding]::Unicode.GetBytes($text);
#Converts to Base 64.
$encodedText =[Convert]::ToBase64String($bytes);

#Makes encoding URL safe.
$urlSafe1 = $encodedText.replace('+', '-');
$urlSafe2 = $urlSafe1.replace('/', '_');
$urlSafe3 = $urlSafe2.replace('=', '*');

return $urlSafe3;
}  

Function Mail-Output
{
Param(
[String]$subject
)

#Acquires access token.
$accessToken = Refresh-AccessToken;  
#Sends subject for MIMEB64 encoding
$text = Set-MIMEBase64Encoded -subject $subject;

#Requests sends email according to parameters.
$messages = Invoke-WebRequest -Uri ("https://www.googleapis.com/gmail/v1/users/me/messages/send?access_token=$accessToken&raw=$text") -Method Post; 
Write-Output $messages
}

Mail-Output -subject "Hope this works!"

So, what I am trying to do here is send a properly formatted MIME (RFC 2822 compliant) email encoded in URL safe base64 through Invoke-WebRequest in powershell. This sample should work, but the issue seems to be that Gmail is not actually accepting email sends in this format.

like image 580
DeepS1X Avatar asked Jan 29 '17 02:01

DeepS1X


People also ask

How do I automate Gmail API?

Setup the Gmail API The MFA service check will access the Gmail account to get the MFA code requested from the web page. First, we have to enable the API and authenticate to Gmail. This can be done by following their Quickstart guide. Click the “ENABLE THE GMAIL API” form button to generate the credentials.


2 Answers

Any reasons not to use Send-MailMessage ?. If not you can try this example:

$From = "[email protected]"
$To = "[email protected]"
$Cc = "[email protected]"
$Attachment = "C:\temp\Some random file.txt"
$Subject = "Email Subject"
$Body = "Insert body text here"
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
Send-MailMessage -From $From -to $To -Cc $Cc -Subject $Subject `
    -Body $Body -SmtpServer $SMTPServer -port $SMTPPort -UseSsl `
    -Credential (Get-Credential) -Attachments $Attachment
like image 175
Moerwald Avatar answered Nov 04 '22 06:11

Moerwald


After working on this for awhile I finally found the missing piece. I was not converting my Base64 encoded string (my mail message) to JSON and I wasn't including it in the Invoke-RestMethod properly.

I finally found the missing piece here: https://github.com/thinkAmi/PowerShell_misc/blob/master/gmail_api/gmail_sender.ps1. Here is the snippet that pointed me in the right direction.

$body = @{ "raw" = $raw; } | ConvertTo-Json
$uri = "https://www.googleapis.com/gmail/v1/users/me/messages/send?access_token=$AccessToken"
$result = Invoke-RestMethod $uri -Method POST -ErrorAction Stop -Body $body -ContentType "application/json"

Once I had that I was able to cobble together a solution for sending through the Gmail API using Powershell.

I found some very helpful code on this SO question (the code can be found here) that helped me get the correct OAuth access tokens.

Here is a working solution (it needs to be cleaned up):

Function Encode-Base64Url([string]$MsgIn) 
{
    $InputBytes =  [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($MsgIn))
    
    # "Url-Safe" base64 encodeing
    $InputBytes = $InputBytes.Replace('+', '-').Replace('/', '_').Replace("=", "")
    return $InputBytes
}

Add-Type -Path "C:\path\to\AE.Net.Mail.dll" # We are using AE.Net.Mail to create our message. https://github.com/andyedinborough/aenetmail
Add-Type -AssemblyName System.IO
Add-Type -AssemblyName System.Text.Encoding

$ToEmail = "[email protected]"
$FromEmail = "[email protected]"
#  From https://gist.github.com/LindaLawton/55115de5e8b366be3969b24884f30a39
#  Setup:
#
#  Step 1: create new project on https://console.developers.google.com.
#  Step 2: Create oauth credentials type native or other.
#          Save the client id and secret. 
#  Step 3: Enable the api you are intersted in accessing.
#          Look up what scopes you need for accssing this api,
#  Step 4: Using the client id, and client secret from the 
#
#
# Inital Authenticate:  Authentication must be done the first time via a webpage create the link you will need.  More then one scope can be added simply by seporating them with a comama
#     Place it in a webbrowser. 
#
#    https://accounts.google.com/o/oauth2/auth?client_id={CLIENT ID}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope={SCOPES}&response_type=code
#    Change Scopes to https://www.googleapis.com/auth/gmail.send
#                     https://www.googleapis.com/auth/gmail.readonly
#                     https://mail.google.com/
#
#    Copy the authencation code and run the following script.  
#      note: AuthorizationCode can only be used once you will need to save the refresh token returned to you.  

$ClientID = "Your Client ID"
$secret = "Your Client Secret"
$RedirectURI = "urn:ietf:wg:oauth:2.0:oob"
$AuthorizationCode = 'Your Authorization Code'

$tokenParams = @{
      client_id=$ClientID;
      client_secret=$secret;
      code=$AuthorizationCode;
      grant_type='authorization_code';
      redirect_uri=$RedirectURI
    }

$token = Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Method POST -Body $tokenParams | ConvertFrom-Json

# Use refresh token to get new access token
# The access token is used to access the api by sending the access_token parm with every request. 
# Access tokens are only valid for an hour, after that you will need to request a new one using your refresh_token
$refreshToken = $token.refresh_token  

$RefreshTokenParams = @{
      client_id=$ClientID;
      client_secret=$secret;
      refresh_token=$refreshToken;
      grant_type='refresh_token';
    }

$RefreshedToken = Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Method POST -Body $refreshTokenParams | ConvertFrom-Json

$AccessToken = $RefreshedToken.access_token

# Compose and send an email using the access token
$From = New-Object MailAddress($FromEmail)
$To = New-Object MailAddress($ToEmail)

$Msg = New-Object AE.Net.Mail.MailMessage
$Msg.To.Add($To)
$Msg.ReplyTo.Add($From) # Important so email doesn't bounce 
$Msg.From = $From
$Msg.Subject = "Sent through the Gmail API using Powershell"
$Msg.Body = "Hello, world from Gmail API using Powershell!"

$MsgSW = New-Object System.IO.StringWriter
$Msg.Save($MsgSW)

$EncodedEmail = Encode-Base64Url $MsgSW

# Found this gem here: https://github.com/thinkAmi/PowerShell_misc/blob/master/gmail_api/gmail_sender.ps1
$Content = @{ "raw" = $EncodedEmail; } | ConvertTo-Json

$Result = Invoke-RestMethod -Uri "https://www.googleapis.com/gmail/v1/users/me/messages/send?access_token=$AccessToken" -Method POST -ErrorAction Stop -Body $Content -ContentType "Application/Json"

if($Result)
{
    Write-Host $Result
}
else
{
    Write-Host "Error sending email"
}
like image 30
mack Avatar answered Nov 04 '22 08:11

mack