使用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
相关产品推荐
相关产品推荐

