Google OAuth登录错误:凭据无效

时间:2013-12-18 19:52:37

标签: ios google-oauth gmail-imap mailcore2 gtm-oauth2

我有一个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令牌停止工作或如何解决这个问题。由于用户可以保存多个帐户,我想知道是否需要为核心数据中的每个帐户保存刷新令牌,如果访问令牌停止工作则使用该令牌?

1 个答案:

答案 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环境来测试它。