2

我有一个 iPad 应用程序,它允许用户使用 OAuth2 登录到他们的 Gmail 帐户。至此,登录过程和电子邮件获取成功。但是,当应用程序关闭并在(长时间)后重新打开时,会产生“无效凭据”错误,即使之前使用相同凭据的登录成功。

登录流程:1) 用户使用 OAuth 2 登录到 gmail。2) GTMOAuth2Authentication 对象提供的用户电子邮件地址和 oAuthToken 保存到核心数据中以供将来登录。3) IMAP 会话是使用保存的电子邮件地址和 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 令牌停止工作或如何解决这个问题。由于用户能够保存多个帐户,我想知道是否需要将每个帐户的刷新令牌保存在核心数据中,并在访问令牌停止工作时使用它?

4

1 回答 1

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

于 2013-12-18T21:29:40.923 回答