How to use a Google offline access token to authenticate in background on iOS?
authentication, google-oauth, ios, iphone, objective-c
Solution
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.
Problem
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: - Google OAuth2 client - Google API objective-c client Thanks for reading!