You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SMTP与XOAUTH2发送Gmail时的Scope权限困惑及咨询

用Gmail XOAUTH2实现SMTP邮件发送(避免生产验证)

问题背景

原本应用使用账号密码验证访问SMTP服务器发邮件,现在改用自己的Gmail账号(booboo@gmail.com)通过XOAUTH2实现。由于仅给自己发邮件,不想走谷歌生产应用验证流程,但切换到更受限的https://www.googleapis.com/auth/gmail.send scope后,出现535-5.7.8 Username and Password not accepted错误,实际并未使用密码验证。

问题根源

添加gmail.send scope后应用进入测试模式,但未添加自己为测试用户,导致授权流程不通过;同时初始token使用的是https://mail.google.com/ scope,和后续发送邮件的scope不一致,引发权限冲突。

解决方案

1. 添加测试用户

进入Google Cloud Console的「OAuth同意屏幕」页面,找到「测试用户」板块,点击「添加用户」,输入自己的邮箱booboo@gmail.com并保存。测试模式下只有添加的测试用户才能正常使用应用权限。

2. 统一使用gmail.send scope重新生成token

确保获取初始token和发送邮件的代码使用完全一致的gmail.send scope,避免权限不匹配:

生成初始token代码

from google_auth_oauthlib.flow import InstalledAppFlow

SCOPES = ['https://www.googleapis.com/auth/gmail.send']

def get_initial_credentials(*, token_path, credentials_path):
        flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)
        creds = flow.run_local_server(port=0)

        with open(token_path, 'w') as f:
            f.write(creds.to_json())

if __name__ == '__main__':
    get_initial_credentials(token_path='token.json', credentials_path='credentials.json')

运行此代码,通过浏览器完成授权(此时不会出现未验证应用的限制,因为你已经是测试用户),生成新的token.json。

3. 修正发送邮件代码

确保代码中scope一致,发件人使用完整邮箱地址,XOAuth2字符串生成正确:

import smtplib
from email.mime.text import MIMEText
import base64
import json
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials

SCOPES = ['https://www.googleapis.com/auth/gmail.send']

def get_credentials(token_path):
    with open(token_path) as f:
        creds = Credentials.from_authorized_user_info(json.load(f), SCOPES)
    if not creds.valid:
        creds.refresh(Request())
        with open(token_path, 'w') as f:
            f.write(creds.to_json())
    return creds

def generate_OAuth2_string(access_token, username):
    auth_string = f'user={username}\1auth=Bearer {access_token}\1\1'
    return base64.b64encode(auth_string.encode('utf-8')).decode('ascii')

message = MIMEText('I need lots of help!', "plain")
message["From"] = 'booboo@gmail.com'
message["To"] = 'booboo@gmail.com'
message["Subject"] = 'Help needed with Gmail'

creds = get_credentials('token.json')
xoauth_string = generate_OAuth2_string(creds.token, 'booboo@gmail.com')

with smtplib.SMTP('smtp.gmail.com', 587) as conn:
    conn.starttls()
    conn.docmd('AUTH', 'XOAUTH2 ' + xoauth_string)
    conn.sendmail('booboo@gmail.com', ['booboo@gmail.com'], message.as_string())

关键注意事项

  • 测试模式下必须添加自己为测试用户,否则会触发权限验证错误。
  • 全程使用相同的scope,不要混用mail.google.com/和gmail.send,否则token权限不匹配。
  • sendmail的第一个参数必须是完整的邮箱地址,不能只填用户名前缀。

内容的提问来源于stack exchange,提问作者Booboo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 12:02:04