So far the OAuth2 authentication could be performed with embedded browsers in iOS and Android. However Google has decided that they want apps to use the default browser instead now. One reason is that the default browser might already know the user due to cookies and can shorten the authentication process. The other reason might be that apps could have theoretically captured the entered password when using an embedded browser. Anyway to switch an iOS app from the old authentication method using GTMOAuth2ViewControllerTouch to the new using OIDAuthorizationRequest requires only a few steps due to the useful library GTMAppAuth:
- Create a file called “Podfile” and use it to download and install GTMAppAuth:
target 'YourProjectName' do platform :ios, '7.0' pod 'GTMAppAuth' end
- Install Cocoapods if necessary and then run
pod install
- Open the created Xcode workspace file. Delete the following old files if you have them:
GTMGatherInputStream.h GTMGatherInputStream.m GTMMIMEDocument.h GTMMIMEDocument.m GTMReadMonitorInputStream.h GTMReadMonitorInputStream.m
- Add a property to your AppDelegate.h file:
@property(nonatomic, retain) id
currentAuthorizationFlow; - Add a handler to your AppDelegate.m file in the application:openURL:sourceApplication:annotation: method. Put this right at the start of the method:
if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { _currentAuthorizationFlow = nil; return YES; }
- Now you are ready to replace your old authentication code which used GTMOAuth2ViewControllerTouch with the new code (taken partially from the GTMAppAuth website):
OIDServiceConfiguration *configuration = [GTMAppAuthFetcherAuthorization configurationForGoogle]; OIDAuthorizationRequest *request = [[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:GOOGLE_CLIENT_ID clientSecret:GOOGLE_CLIENT_SECRET scopes:@[OIDScopeEmail, @"https://www.googleapis.com/auth/calendar"] redirectURL:[NSURL URLWithString:@"com.example.yourapp:/oauthredirect"] responseType:OIDResponseTypeCode additionalParameters:nil] autorelease]; // performs authentication request AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; appDelegate.currentAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request presentingViewController:self callback:^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { if (authState) { // Creates the GTMAppAuthFetcherAuthorization from the OIDAuthState. GTMAppAuthFetcherAuthorization *authorization = [[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState] autorelease];
- Using the authorization result you can now call your existing handler:
[self viewControllerFinishedWithAuth:authorization error:error accessToken:authState.lastTokenResponse.accessToken]; NSLog(@"Got authorization tokens. Access token: %@", authState.lastTokenResponse.accessToken); } else { NSLog(@"Authorization error: %@", [error localizedDescription]); } }]; } - (void)viewControllerFinishedWithAuth:(id
)auth error:(NSError *)error accessToken:(NSString *)accessToken { if (error == nil) { // OAuth2 Login was successful // Serialize to Keychain [GTMOAuth2KeychainCompatibility saveAuthToKeychainForName:keychainNameOAuth2 authentication:auth]; Now you can use “accessToken” to access Google services. And due to GTMOAuth2KeychainCompatibility you can keep your existing functions that load the authentication data from the keychain.
- In the Info.plist file of your app you have to configure the “com.example.yourapp” URL mentioned above so that the browser can open your app and send you the result:
CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLSchemes com.example.yourapp
So that’s all you have to change for an existing iOS app. You can find further information in Google’s blogpost.