【问题标题】:Right way to refresh Gmail API access token in objective-c在objective-c中刷新Gmail API访问令牌的正确方法
【发布时间】:2016-12-27 11:53:14
【问题描述】:

我通过该方法获得访问令牌:

- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
  finishedWithAuth:(GTMOAuth2Authentication *)authResult
             error:(NSError *)error {
if (error != nil) {
    [self showAlert:@"Authentication Error" message:error.localizedDescription];
    self.service.authorizer = nil;
}
else {
    self.service.authorizer = authResult;

    NSLog(@"Token: %@ id: %@", authResult.accessToken, authResult.userID);
    [self makeGmailLabelVisibleWithToken:authResult.accessToken]; //make an authorized request to gmailAPI with the access token

    [self dismissViewControllerAnimated:YES completion:nil];

  }
}

所以,经过身份验证后它工作正常,但过了一段时间它停止工作(我猜是因为令牌已过期)。另外,如果我使用

[authResult refreshToken]

而不是

authResult.accessToken

这行不通。

那么刷新 Gmail 访问令牌的正确方法是什么,我应该使用哪种方法呢?

P.S:documentation 表示

- (void) refreshTokensWithHandler:(GIDAuthenticationHandler)handler

应该有帮助,但我没有找到任何样本。

【问题讨论】:

    标签: ios objective-c gmail google-oauth gmail-api


    【解决方案1】:

    要获取刷新令牌,您必须为您的应用程序enable server-side APIA access。 “要获取服务器的访问令牌和刷新令牌,您可以请求服务器交换这两个令牌的一次性授权代码。您可以通过指定服务器的客户端 ID 以及其他 GIDSignIn 参数来请求一次性代码. 成功连接用户后,您会发现一次性代码作为 auth 参数 server_code 可通过 finishedWithAuth:error 处理程序访问。"

    1. 按照开始集成中的说明配置 iOS 应用项目。
    2. 定义您的应用委托的 application:didFinishLaunchingWithOptions: 方法,如上文启用登录中所述,但对于此实现,您将设置 serverClientID 属性,如下所示。
    -(BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
      [GIDSignIn sharedInstance].clientID = @"APP_CLIENT_ID";
      [GIDSignIn sharedInstance].serverClientID = @"SERVER_CLIENT_ID";
    
      // Additional scopes, if any
      // [GIDSignIn sharedInstance].scopes = @[ @"other_scope" ];
    
      return YES;
    }
    
    1. 用户登录后,获取一次性授权码:
    -(void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user
        withError:(NSError *)error {
      // Perform any operations on signed in user here.
      // user.serverAuthCode now has a server authorization code!
    }
    
    1. 使用 HTTPS POST 将 serverAuthCode 字符串安全地传递到您的服务器。
    2. 在应用程序的后端服务器上,交换身份验证代码以获取访问和刷新令牌。使用访问令牌代表用户调用 Google API,并且可以选择存储刷新令牌以在访问令牌过期时获取新的访问令牌。

    您可以使用HTTP/REST 调用。

    这是一个 Python 中的 HTTP 调用,只需使用 Objective-C 等效项。

    import json
    import flask
    import requests
    
    
    app = flask.Flask(__name__)
    
    CLIENT_ID = '123456789.apps.googleusercontent.com'
    CLIENT_SECRET = 'abc123'  # Read from a file or environmental variable in a real app
    SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly'
    REDIRECT_URI = 'http://example.com/oauth2callback'
    
    
    @app.route('/')
    def index():
      if 'credentials' not in flask.session:
        return flask.redirect(flask.url_for('oauth2callback'))
      credentials = json.loads(flask.session['credentials'])
      if credentials['expires_in'] <= 0:
        return flask.redirect(flask.url_for('oauth2callback'))
      else:
        headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
        req_uri = 'https://www.googleapis.com/drive/v2/files'
        r = requests.get(req_uri, headers=headers)
        return r.text
    
    
    @app.route('/oauth2callback')
    def oauth2callback():
      if 'code' not in flask.request.args:
        auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
                    '&client_id={}&redirect_uri={}&scope={}').format(CLIENT_ID, REDIRECT_URI, SCOPE)
        return flask.redirect(auth_uri)
      else:
        auth_code = flask.request.args.get('code')
        data = {'code': auth_code,
                'client_id': CLIENT_ID,
                'client_secret': CLIENT_SECRET,
                'redirect_uri': REDIRECT_URI,
                'grant_type': 'authorization_code'}
        r = requests.post('https://www.googleapis.com/oauth2/v4/token', data=data)
        flask.session['credentials'] = r.text
        return flask.redirect(flask.url_for('index'))
    
    
    if __name__ == '__main__':
      import uuid
      app.secret_key = str(uuid.uuid4())
      app.debug = False
      app.run()
    

    【讨论】:

    • 感谢您的回答,@noogui,但是如果我不使用服务器该怎么办(我现在改用 Firebase)?
    • 我想我应该使用像[obj refreshTokensWithHandler :^(GIDAuthentication *authentication, NSError *error){ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"new token: %@", authentication.accessToken); }); }]; 这样的东西,但是这个处理程序没有被执行,我不知道为什么:) @noogui
    • @ArtStyle 尝试检查this thread,它指的是 Firebase 重新身份验证。
    【解决方案2】:

    所以,这实际上很简单。要刷新令牌,您需要使用以下代码:

    self.service = [[GTLServiceGmail alloc] init];
    self.service.authorizer =
    [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
                                                          clientID:kClientID
                                                      clientSecret:nil];
    
    [[GIDSignIn sharedInstance] setScopes:[NSArray arrayWithObject: @"https://www.googleapis.com/auth/plus.me"]];
    [GIDSignIn sharedInstance].clientID = kClientID;
    
    GTMOAuth2Authentication *auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
                                                                                          clientID:kClientID
                                                                                      clientSecret:nil];
    
    NSLog(@"accessToken: %@", auth.accessToken); //If the token is expired, this will be nil
    
    // authorizeRequest will refresh the token, even though the NSURLRequest passed is nil
    [auth authorizeRequest:nil
         completionHandler:^(NSError *error) {
             if (error) {
                 NSLog(@"error: %@", error);
             }
             else {
                 [USER_CACHE setValue:auth.accessToken forKey:@"googleAccessToken"];
             }
         }];
    

    例如,您可以将其粘贴到 ViewDidLoad 方法中。因此,执行此代码后,您将在 UserDefaults 中获得有效的访问令牌(在我的示例中为 USER_CAHCE)。

    【讨论】:

      猜你喜欢
      • 2016-11-30
      • 2015-08-21
      • 2019-02-16
      • 2016-08-28
      • 2016-12-13
      • 1970-01-01
      • 2019-04-15
      • 2017-08-11
      • 2012-08-23
      相关资源
      最近更新 更多