How to send an email with Python without enabling 2-fa and allow less secure app options in the sender Gmail account?

huangapple go评论69阅读模式
英文:

How to send an email with Python without enabling 2-fa and allow less secure app options in the sender Gmail account?

问题

我正在尝试开发一个功能,用于从具有自己域名的Gmail帐户发送电子邮件。例如:person1@companyname.com

要求是我不能在Google帐户中启用两因素身份验证,也不能允许不安全的应用程序。

那么,最佳方法是什么?我看过YouTube上的教程,但它们使用credential.json或client.json文件进行身份验证,没有解释如何获取这些文件或它们的内容的正确方法。

这是我目前的代码:

# 发送邮件
try:
    password = '应将app密码放在这里,但我不能使用它'
    sender_email = 'person1@companyname.com'
    recipient_email = ['person2@companyname.com', 'testemail@gmail.com']
    subject = 'blablba'
    body = 'blabla'
    em = EmailMessage()
    em['From'] = sender_email
    em['To'] = ", ".join(recipient_email)
    em['subject'] = subject
    em.set_content(body)

    context = ssl.create_default_context()

    with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp:
        smtp.login(sender_email, password)
        smtp.sendmail(sender_email, recipient_email, em.as_string())

except Exception as e:
    errors = errores + '发送邮件时出错:' + str(e)
    logging.warning(errors)

请注意,代码中的密码部分已被替换为示例文本。你需要将实际的应用程序密码放在这里。

英文:

I am trying to develop a feature for sending an email from an gmail account with an own domain. For example: person1@companyname.com

The requirement is that I can't enable the 2 factor authentication in the google account and allow less secure apps neither.

So, what will be the best approach for this? I have seen a tutorial on Youtube but they use a credential.json or client.json files for authentication without explaining the proper way to get these files or their contents.

This is the code I have so far:

 # Sending email
           try:
               password = 'appPasswordIsSupposedtoBePlacedHereButIAmNotAllowedToUseit'
               sender_email = 'person1@companyname.com'
               recipient_email = ['person2@companyname.com', 'testemail@gmail.com'] 
               subject = 'blablba'
               body = 'blabla'
               em= EmailMessage()
               em['From']= sender_email
               em['To'] = ", ".join(recipient_email)
               em['subject'] = subject
               em.set_content(body)

               context = ssl.create_default_context()

               with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp:
                   smtp.login(sender_email, password)
                   smtp.sendmail(sender_email, recipient_email, em.as_string())

           except Exception as e:
               errors = errores + ' Error while sending the email: ' + str(e)
               logging.warning(errors)

答案1

得分: 1

  1. 创建一个Google Workspace帐户以供您的域名使用,然后您可以使用服务帐户和域范围的委托来使用Gmail API进行此操作。

  2. 使用Xoauth2来授权用户并存储刷新令牌。

示例Xoauth2

from __future__ import print_function

import base64
import os.path
import smtplib

from email.mime.text import MIMEText
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow

# 如果修改这些范围,请删除token.json文件。
SCOPES = ['https://mail.google.com/']

# 用户令牌存储
USER_TOKENS = 'token.json'

# 应用程序凭据
CREDENTIALS = 'C:\YouTube\dev\credentials.json'

def getToken() -> str:
    creds = None

    # token.json文件存储用户的访问和刷新令牌,在首次授权流程完成时将自动生成。
    if os.path.exists(USER_TOKENS):
        creds = Credentials.from_authorized_user_file(USER_TOKENS, SCOPES)
        creds.refresh(Request())
    # 如果没有(有效的)凭据可用,让用户登录。
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS, SCOPES)
            creds = flow.run_local_server(port=0)
        # 保存凭据以供下次运行使用
        with open(USER_TOKENS, 'w') as token:
            token.write(creds.to_json())

    return creds.token

def generate_oauth2_string(username, access_token) -> str:
    auth_string = 'user=' + username + '\1auth=Bearer ' + access_token + '\1\1'
    return base64.b64encode(auth_string.encode('ascii')).decode('ascii')

def send_email(host, port, subject, msg, sender, recipients):
    access_token = getToken()
    auth_string = generate_oauth2_string(sender, access_token)

    msg = MIMEText(msg)
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    server = smtplib.SMTP(host, port)
    server.starttls()
    server.docmd('AUTH', 'XOAUTH2 ' + auth_string)
    server.sendmail(sender, recipients, msg.as_string())
    server.quit()

def main():
    host = "smtp.gmail.com"
    port = 587

    user = "xxx@gmail.com"
    recipient = "xxx@gmail.com"
    subject = "Test email Oauth2"
    msg = "Hello world"
    sender = user
    recipients = [recipient]
    send_email(host, port, subject, msg, sender, recipients)

if __name__ == '__main__':
    main()

访问令牌和刷新令牌将存储在'token.json'中。只需在Google开发者控制台上创建已安装的应用凭据,并将应用程序设置为生产模式,如果只有单个用户,您无需进行验证。

英文:

You actually have two options.

  1. create a google workspace account for your domain, then you can use a service account and domain wide deligation to do this with the gmail api.

  2. Use Xoauth2 to authorize the user and store the refresh token.

2023: How I access GMail SMTP without enabling 2fa. (Python oauth2)

Sample Xoauth2

from __future__ import print_function
import base64
import os.path
import smtplib
from email.mime.text import MIMEText
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
# user token storage
USER_TOKENS = 'token.json'
# application credentials
CREDENTIALS = 'C:\YouTube\dev\credentials.json'
def getToken() -> str:
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists(USER_TOKENS):
creds = Credentials.from_authorized_user_file(USER_TOKENS, SCOPES)
creds.refresh(Request())
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS, SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open(USER_TOKENS, 'w') as token:
token.write(creds.to_json())
return creds.token
def generate_oauth2_string(username, access_token) -> str:
auth_string = 'user=' + username + '\1auth=Bearer ' + access_token + '\1\1'
return base64.b64encode(auth_string.encode('ascii')).decode('ascii')
def send_email(host, port, subject, msg, sender, recipients):
access_token = getToken()
auth_string = generate_oauth2_string(sender, access_token)
msg = MIMEText(msg)
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
server = smtplib.SMTP(host, port)
server.starttls()
server.docmd('AUTH', 'XOAUTH2 ' + auth_string)
server.sendmail(sender, recipients, msg.as_string())
server.quit()
def main():
host = "smtp.gmail.com"
port = 587
user = "xxx@gmail.com"
recipient = "xxx@gmail.com"
subject = "Test email Oauth2"
msg = "Hello world"
sender = user
recipients = [recipient]
send_email(host, port, subject, msg, sender, recipients)
if __name__ == '__main__':
main()

The access token and refresh token will be stored in 'token.json'. Just create installed app credentials on google developer console and set the app to production if its single user you dont need to verify it .

huangapple
  • 本文由 发表于 2023年6月12日 03:15:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76452150.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定