使用XOAuth2认证Gmail SMTP时报用户名密码不接受错误
问题背景
Google近期移除了低安全性应用的访问支持,由于不希望采用应用专用密码方案,我尝试对接基于Xoauth2协议的SMTP服务授权,将其作为更安全、可持续的鉴权方式。
我编写了简单控制台应用开展测试,实现逻辑如下:
- 使用MailKit类库开发
- 连接Google SMTP服务器
- 采用Xoauth2协议鉴权
- 已在Google Cloud控制台创建已安装应用类型凭证
授权流程看似运行正常,可以成功获取到access token,但SMTP服务器始终拒绝认证请求,怀疑是token的使用方式存在错误。
测试代码如下:
using Google.Apis.Auth.OAuth2; using Google.Apis.Util.Store; using MailKit.Net.Smtp; using MailKit.Security; using MimeKit; var path = @"C:\YouTube\dev\credentials.json"; var scopes = new[] { "email" }; var userName = "test2"; var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromFile(path).Secrets, scopes, userName, CancellationToken.None, new FileDataStore(Directory.GetCurrentDirectory(), true)).Result; credential.GetAccessTokenForRequestAsync(); var message = new EmailMessage() { From = "xxx@gmail.com", To = "xxx@gmail.com", MessageText = "test", Subject = "test" }; try { using (var client = new SmtpClient()) { client.Connect("smtp.gmail.com", 465, true); var oauth2 = new SaslMechanismOAuth2 ("xxx@gmail.com", credential.Token.AccessToken); await client.AuthenticateAsync (oauth2, CancellationToken.None); client.Send(message.GetMessage()); client.Disconnect(true); } } catch (Exception ex) { int i = 1; // 此处抛出MailKit.Security.AuthenticationException异常 } public class EmailMessage { public string To { get; set; } public string From { get; set; } public string Subject { get; set; } public string MessageText { get; set; } public MimeMessage GetMessage() { var body = MessageText; var message = new MimeMessage(); message.From.Add(new MailboxAddress("test", From)); message.To.Add(new MailboxAddress("test", To)); message.Subject = Subject; message.Body = new TextPart("plain") { Text = body }; return message; } }
运行代码时在认证环节抛出异常,完整错误信息如下:
MailKit.Security.AuthenticationException: 535: 5.7.8 Username and Password not accepted. Learn more at
5.7.8 BadCredentials提示页 da26-20020a056402177a00b004315050d7dfsm6610655edb.81 - gsmtp
at MailKit.Net.Smtp.SmtpClient.AuthenticateAsync(SaslMechanism mechanism, Boolean doAsync, CancellationToken cancellationToken)
at Program.$(String[] args) in C:\YouTube\dev\Gmail\Code\SMTPTesting\SmtpTest\SmtpTestXoauth2\Program.cs:line 38
故障原因
核心错误是OAuth授权范围(scope)配置错误:代码中配置的"email" scope仅能获取用户的邮箱地址基础信息,不包含Gmail SMTP发信权限,用该scope申请到的token没有访问SMTP服务的授权,自然会被服务端判定为无效凭证返回535错误。
除此之外代码还有两处隐患:
- 调用
credential.GetAccessTokenForRequestAsync()时既没有await等待执行完成,也没有接收返回值,直接读取credential.Token.AccessToken可能拿到过期、未刷新完成的无效token - 未在Google Cloud控制台完成对应配置:使用Gmail API相关权限属于敏感受限范围,测试阶段需要将测试用Gmail账号添加到OAuth同意屏的测试用户列表中,同时要确保已经启用Gmail API服务。
修复方案
- 替换授权scope为Gmail SMTP对应的权限值
"https://mail.google.com/",该scope包含SMTP/IMAP/POP3全量邮件操作权限 - 修正token获取逻辑,通过await调用
GetAccessTokenForRequestAsync()获取最新的有效访问token,不要直接读取Token属性下的AccessToken值 - 检查Google Cloud控制台配置:
- 确认已经在API库中启用Gmail API服务
- 进入OAuth同意屏配置页,将用于测试的Gmail账号添加为测试用户
- 如果应用要正式发布,需要完成OAuth应用的安全审核,否则普通用户授权时会被拦截
修复后的核心代码片段:
// 替换为正确的SMTP授权scope var scopes = new[] { "https://mail.google.com/" }; var userName = "test2"; var credential = GoogleWebAuthorizationBroker.AuthorizeAsync( GoogleClientSecrets.FromFile(path).Secrets, scopes, userName, CancellationToken.None, new FileDataStore(Directory.GetCurrentDirectory(), true) ).Result; // await获取最新有效token var accessToken = await credential.GetAccessTokenForRequestAsync(); // SMTP连接与发信逻辑 using (var client = new SmtpClient()) { client.Connect("smtp.gmail.com", 465, true); var oauth2 = new SaslMechanismOAuth2 ("xxx@gmail.com", accessToken); await client.AuthenticateAsync (oauth2, CancellationToken.None); client.Send(message.GetMessage()); client.Disconnect(true); }
内容的提问来源于stack exchange,提问作者Linda Lawton - DaImTo

