如何使用Google离线访问令牌在iOS后台进行身份验证?

时间:2014-04-15 19:36:32

标签: ios iphone objective-c authentication google-oauth

简短版:

我想使用Google OAuth2 client for Objective C创建一个有效的GTMOAuth2Authentication对象供我在我的应用中使用,我可以从后端获取一个离线访问令牌。我怎么能做到这一点?

我的情况:

我的应用使用Google Analytics只读范围来获取有关用户网站的一些数据。我完成此操作的方法是使用Google OAuth2客户端为Objective C提供的GTMOAuth2ViewControllerTouch ViewController登录。然后我会向我提供有效的GTMOAuth2Authentication对象,我可以使用该对象查询Google Analytics通过Google API client

现在,我们不希望为用户提供Google Analytics访问权限(有些人没有Google帐户,一般情况下,我们希望通过应用程序保持信息简单)。这意味着我们必须使用我们的帐户登录(可以访问所有网站的Google Analytics数据)。显然我们无法向用户提供我们的凭据,因此我们必须找到解决方案。

我的计划:

我认为可以通过从我们的后端(通过SSL加密)请求我们的(离线访问)令牌字符串来解决此问题,将其保存到用户的钥匙串,并在应用程序中进一步使用它来查询Analytics。然后我们让用户登录我们的服务(这样我们就可以确定用户可以访问哪些网站),并显示数据。

我的问题:

我到处搜索,查看了Google的(非常薄的)文档,检查了GTMOAuth2Authentication源代码,但我似乎无法解决问题。在我看来会有这样的解决方案(因为我在CMS中使用类似的方法让用户发布到我们的Facebook墙上),所以我在这里缺少什么?

当前登录代码:

GTMOAuth2ViewControllerTouch *viewController = [[GTMOAuth2ViewControllerTouch alloc]
                                                    initWithScope:scope
                                                    clientID:kMyClientID
                                                    clientSecret:kMyClientSecret
                                                    keychainItemName:kKeychainItemName
                                                    completionHandler:
    ^(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error) {
        if (error != nil) {
            // Authentication failed
            DebugLog(@"Failed!");
        } else {
            // Authentication succeeded
            DebugLog(@"Success!");

            // Update interface
            self.loginButton.hidden = YES;

            self.authentication = auth;
        }
    }]; 

我尝试了什么:

我尝试手动创建GTMOAuth2Authentication对象并自行设置所有参数(范围,clientid,secretid,访问令牌,刷新令牌,callbackuri,令牌url等),但我得到了返回401:登录我尝试将对象用于查询时所需的错误。所以我猜这不是这样做的方法。

链接:

感谢阅读!

2 个答案:

答案 0 :(得分:6)

您不需要GTMOAuth2Authentication实例。 您需要的是创建自己的类来实现GTMFetcherAuthorizationProtocol协议,并将其指定为您的授权者。

MyAuth *myAuth = [[MyAuth alloc] initWithAccessToken:accessToken];
googleService.authorizer = myAuth;

此类需要根据您已有的访问令牌授权请求。 这个类的代码应该与此类似。

@interface MyAuth : NSObject<GTMFetcherAuthorizationProtocol>     
    @property (copy, nonatomic) NSString *accessToken;
    @property (copy, readonly) NSString *userEmail;
@end

@implementation MyAuth

- (void)authorizeRequest:(NSMutableURLRequest *)request
                delegate:(id)delegate
       didFinishSelector:(SEL)sel {
    if (request) {
        NSString *value = [NSString stringWithFormat:@"%s %@", GTM_OAUTH2_BEARER, self.accessToken];
        [request setValue:value forHTTPHeaderField:@"Authorization"];
    }

    NSMethodSignature *sig = [delegate methodSignatureForSelector:sel];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
    [invocation setSelector:sel];
    [invocation setTarget:delegate];
    [invocation setArgument:(__bridge void *)(self) atIndex:2];
    [invocation setArgument:&request atIndex:3];
    [invocation invoke];
}

- (void)authorizeRequest:(NSMutableURLRequest *)request
       completionHandler:(void (^)(NSError *error))handler {
    if (request) {
        NSString *value = [NSString stringWithFormat:@"%@ %@", @"Bearer", self.accessToken];
        [request setValue:value forHTTPHeaderField:@"Authorization"];
    }
}

- (BOOL)isAuthorizedRequest:(NSURLRequest *)request {
    return NO;
}

- (void)stopAuthorization {
}

- (void)stopAuthorizationForRequest:(NSURLRequest *)request {
}

- (BOOL)isAuthorizingRequest:(NSURLRequest *)request {
    return YES;
}

希望它有所帮助。

答案 1 :(得分:1)

您需要创建自己的实现GTMFetcherAuthorizationProtocol协议的类,并将其分配为您的授权者。

这是Xcode 10.1(2019)更新的固定的非崩溃版本:

NSString *accessToken = "ya29.c.Elu-Bmq2eNaGPnIe3-Q4GuTAhuQZmyC6ylm6zCROBtyKEJQFJdlIBTUVlUjOOoaT0cQae_OGdxNM4ayRT_0yg121kD8ouX4SGbllPWRkiGHRbsZRuMPX2QCgMoIO";
MyAuth *auth = [[MyAuth alloc] initWithAccessToken:accessToken];
googleService.authorizer = auth;

MyGTMFetcherAuthorization.m

#import <Foundation/Foundation.h>
#import <GTMSessionFetcher/GTMSessionFetcher.h>

@interface MyAuth : NSObject<GTMFetcherAuthorizationProtocol>
+ (MyAuth *)initWithAccessToken:(NSString *)accessToken;
@end

// Until all OAuth 2 providers are up to the same spec, we'll provide a crude
// way here to override the "Bearer" string in the Authorization header
#ifndef GTM_OAUTH2_BEARER
#define GTM_OAUTH2_BEARER "Bearer"
#endif

@interface MyAuth ()
@property (strong, nonatomic) NSString *accessToken;
@end

@implementation MyAuth

+ (MyAuth *)initWithAccessToken:(NSString *)accessToken {
    MyAuth *auth = [[MyAuth alloc] init];
    auth.accessToken = [accessToken copy];
    return auth;
}

- (void)authorizeRequest:(NSMutableURLRequest *)request
                delegate:(id)delegate
       didFinishSelector:(SEL)sel {
    [self setTokeToRequest:request];

    NSMethodSignature *sig = [delegate methodSignatureForSelector:sel];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
    [invocation setSelector:sel];
    [invocation setTarget:delegate];
    [invocation setArgument:(&self) atIndex:2];
    [invocation setArgument:&request atIndex:3];
    [invocation invoke];
}

- (void)authorizeRequest:(NSMutableURLRequest *)request
   completionHandler:(void (^)(NSError *error))handler {
    [self setTokeToRequest:request];
}

- (void)setTokeToRequest:(NSMutableURLRequest *)request {
    if (request) {
        NSString *value = [NSString stringWithFormat:@"%s %@", GTM_OAUTH2_BEARER, self.accessToken];
        [request setValue:value forHTTPHeaderField:@"Authorization"];
    }
}

- (BOOL)isAuthorizedRequest:(NSURLRequest *)request {
    return NO;
}

- (void)stopAuthorization {
}

- (void)stopAuthorizationForRequest:(NSURLRequest *)request {
}

- (BOOL)isAuthorizingRequest:(NSURLRequest *)request {
    return YES;
}
@end