Google OAuth 登录错误:凭据无效
Posted
技术标签:
【中文标题】Google OAuth 登录错误:凭据无效【英文标题】:Google OAuth Login Error: Invalid credentials 【发布时间】:2014-01-07 03:56:30 【问题描述】:我有一个 iPad 应用程序,它允许用户使用 OAuth2 登录到他们的 Gmail 帐户。至此,登录过程和电子邮件获取成功。但是,当应用程序关闭并在(长时间)后重新打开时,会产生“无效凭据”错误,即使之前使用相同凭据的登录成功。
登录流程: 1) 用户使用 OAuth 2 登录 gmail。 2) GTMOAuth2Authentication 对象提供的用户电子邮件地址和 oAuthToken 保存到核心数据中,以供将来登录。 3) IMAP Session 是使用保存的电子邮件地址和 OAuthToken 创建的。
这里是相关代码
谷歌登录
- (void)gmailOAuthLogin
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
GTMOAuth2ViewControllerTouch *googleSignInController =
[[GTMOAuth2ViewControllerTouch alloc] initWithScope:GmailScope clientID:GmailAppClientID clientSecret:GmailClientSecret keychainItemName:KeychainItemName completionHandler:^(GTMOAuth2ViewControllerTouch *googleSignInController, GTMOAuth2Authentication *auth, NSError *error)
if (error != nil)
//handle error
else
[[ModelManager sharedInstance] authenticateWithEmailAddress:[auth userEmail]
oAuthToken:[auth accessToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account)
//create IMAP session using above arguments
failure:^(NSError *error)
//handle error
];
];
[self presentGoogleSignInController:googleSignInController];
使用 MailCore2 创建 IMAP 会话
- (void)authenticateWithEmailAddress:(NSString *)emailAddress password:(NSString *)password oAuthToken:(NSString *)oAuthToken imapHostname:(NSString *)imapHostname imapPort:(NSInteger)imapPort smtpHostname:(NSString *)smtpHostname smtpPort:(NSInteger)smtpPort success:(void (^)())success failure:(void (^)(NSError *))failure
self.imapSession = [[MCOIMAPSession alloc] init];
self.imapSession.hostname = imapHostname;
self.imapSession.port = imapPort;
self.imapSession.username = emailAddress;
self.imapSession.connectionType = MCOConnectionTypeTLS;
self.imapSession.password = nil;
self.imapSession.OAuth2Token = oAuthToken;
self.imapSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.imapSession.authType;
[self.imapSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type,
NSData * data)
NSLog(@"MCOIMAPSession: [%i] %@", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
];
self.smtpSession = [[MCOSMTPSession alloc] init];
self.smtpSession.hostname = smtpHostname;
self.smtpSession.port = smtpPort;
self.smtpSession.username = emailAddress;
self.smtpSession.connectionType = MCOConnectionTypeTLS;
self.smtpSession.password = nil;
self.smtpSession.OAuth2Token = oAuthToken;
self.smtpSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.smtpSession.authType;
[self.smtpSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type, NSData * data)
NSLog(@"MCOSMTPSession: [%i] %@", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
];
[[self.imapSession checkAccountOperation] start:^(NSError *error)
if (nil == error)
success();
else
failure(error); //FAILS WITH INVALID CREDENTIALS ERROR
];
再一次,上面的代码工作正常,除非应用程序有一段时间没有被使用过。我不确定是否需要刷新 OAuthToken,所以我尝试在启动应用程序时执行以下操作:
GTMOAuth2Authentication *auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:KeychainItemName clientID:GmailAppClientID clientSecret:GmailClientSecret];
BOOL canAuthorize = [auth canAuthorize]; //returns YES
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
[[ModelManager sharedDefaultInstance] authenticateWithEmailAddress:[auth userEmail] oAuthToken:[auth refreshToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account)
//create IMAP session
failure:^(NSError *error)
NSLog(@"failure %@", error);
];
但我仍然遇到同样的错误。我不知道为什么 OAuth 令牌停止工作或如何解决这个问题。由于用户能够保存多个帐户,我想知道是否需要将每个帐户的刷新令牌保存在核心数据中,并在访问令牌停止工作时使用它?
【问题讨论】:
【参考方案1】:(免责声明 - 我不了解 ios 或 gtm-oauth2 库,但我知道 Google 的 OAuth 实现。)
从概念上讲,您确实需要为用户保留刷新令牌。刷新令牌是一种长期存在的凭证,用于(与您的客户端密码一起)获取用于实际 API 调用的短期访问令牌。
如果您预计在短时间内进行多次调用,那么您的应用通常实际上会同时保留刷新令牌和访问令牌(当前访问令牌将持续 1 小时)。
总而言之,看起来 gtm-oauth2 库应该已经在处理这些问题了(看起来像 authForGoogleFromKeychainForName
这样做)。
我认为您需要帮助的是获取可用于启动 IMAP 会话的最新访问令牌。
gtm-oauth2 库确实包含一个authorizeRequest
方法。它获取有关您打算发出的 HTTP 请求的信息并添加适当的授权标头。看起来这段代码将检查访问令牌的状态,并在必要时刷新它。
虽然我知道您无法发出 HTTP 请求(您需要使用 IMAP),但我的建议是将此方法与虚拟 NSMutableURLRequest
一起使用 - 然后,一旦完成,就不要实际发送HTTP 请求,而是检查它添加的标头并从那里提取访问令牌。
见: https://code.google.com/p/gtm-oauth2/wiki/Introduction#Using_the_Authentication_Tokens
希望有所帮助 - 我没有 iOS 环境来测试它。
【讨论】:
感谢您的回复。这是有道理的,只有一点混乱:如果我的应用程序允许用户保存多个 Gmail 帐户并在它们之间来回切换,那么 keychainItemName 是否会随着每个新帐户的添加而被覆盖? (如果这个问题没有意义,请原谅我,我仍在思考这一切是如何运作的)。如果是这样,那么我不确定如何获取特定帐户的身份验证对象以从中提取新令牌。 你的问题很有道理。每个用户帐户都需要为您的应用程序授予单独的刷新令牌。从粗略的代码检查来看,似乎 gtm-oauth2 库一次只能保留一个,但我不能 100% 确定。也许尝试为第二个和后续帐户合成和指定不同的 keychainItemName? 谢谢,我会试试的。这绝对澄清了一些事情。 您找到解决方案了吗?我有同样的问题。我已经尝试调用 authorizeRequest,但即便如此,新的访问令牌也会导致 MailCore 出现 AUTHENTICATIONFAILED 错误。以上是关于Google OAuth 登录错误:凭据无效的主要内容,如果未能解决你的问题,请参考以下文章
请求具有无效的身份验证凭据。预期的 OAuth 2 访问令牌、登录 cookie 或其他有效的身份验证凭据 automl
FCM 推送 - 请求具有无效的身份验证凭据。预期的 OAuth 2 访问令牌、登录 cookie 或其他有效的身份验证凭据
是否可以使用用户和凭据(JWT)登录,而 Oauth2 可以使用 google 登录?