Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
537 views
in Technique[技术] by (71.8m points)

iphone - How to use a Google offline access token to authenticate in background on iOS?

Short version:

I want to use the Google OAuth2 client for Objective C to create a valid GTMOAuth2Authentication object for me to use in my app, with an offline access token I get from my backend. How can I accomplish that?

My situation:

My app uses Analytics read-only scope to get some data about the user's website(s). The way I accomplish this, is by logging in with the GTMOAuth2ViewControllerTouch ViewController, provided by the Google OAuth2 client for Objective C. It then gives me the valid GTMOAuth2Authentication object, that I can use to query Google Analytics via the Google API client.

Now, we don't want to give the users Google Analytics access (some don't have Google accounts and in general, we want to keep the information simple via the app). This means we got to login with our account (that has access to all the websites' Analytics data). Obviously we can't give our users our credentials, so we had to find a solution for that.

My plan:

I think this problem could be solved by requesting our (offline access) token string from our backend (via SSL encryption), saving it to the user's keychain and use it further in the application to query Analytics. We then let the user login at our service (so we can determine which websites the user has access to), and show the data.

My problem:

I have searched everywhere, looked through the (very thin) documentation by Google, inspected the GTMOAuth2Authentication source code, but I cannot seem to wrap my head around the problem. It seems to me there would be a solution like this (because I use a similar approach in our CMS to let a user post to our Facebook wall), so what am I missing here?

The current login code:

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;
        }
    }]; 

What I have tried:

I have tried manually creating a GTMOAuth2Authentication object and setting all the parameters myself (scope, clientid, secretid, access token, refresh token, callbackuri, token url, etc), but I get returned a 401: Login Required error when I try to use the object for queries. So I guess that is not the way to do it.

Links:

Thanks for reading!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You don't need a GTMOAuth2Authentication instance. What you need is to create you own class that implement the GTMFetcherAuthorizationProtocol protocol, and assign it as your authorizer.

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

This class needs to authorize the request based on the access token you already have. The code of this class should be similar to this.

@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;
}

Hope it helps.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...