我有一个iPad应用程序,允许用户使用OAuth2登录他们的Gmail帐户。到目前为止,登录过程和电子邮件提取成功。但是,当应用程序关闭然后在(很长一段)时间后重新打开时,即使先前使用相同凭据的登录成功,也会产生“无效凭据”错误。
登录流程: 1)用户使用OAuth 2登录gmail。 2)GTMOAuth2Authentication对象提供的用户电子邮件地址和oAuthToken将保存到核心数据以供将来登录。 3)使用保存的电子邮件地址和OAuthToken创建IMAP会话。
以下是相关代码
Google登录
- (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令牌停止工作或如何解决这个问题。由于用户可以保存多个帐户,我想知道是否需要为核心数据中的每个帐户保存刷新令牌,如果访问令牌停止工作则使用该令牌?
答案 0 :(得分:0)
(免责声明 - 我不知道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环境来测试它。