GTMAppAuth Documentation

repository·master·Indexed 19 days ago

https://github.com/google/gtmappauth

GTMAppAuth provides an AppAuth-based implementation of GTMFetcherAuthorizationProtocol for Apple platforms (iOS, macOS, tvOS, and watchOS). It enables secure OAuth authorization using the system's default browser, following RFC 8252. The library integrates with Google Toolbox for Mac - Session Fetcher and the Google APIs Client Library for Objective-C For REST, offering session reuse and a migration path from GTMOAuth2.

Tokens
5.7K
Snippets
14
Records
22
Agent score
65%

What's inside GTMAppAuth

  1. Overview of GTMAppAuth for Apple Platforms

    master

    GTMAppAuth is a library designed for iOS, macOS, tvOS, and watchOS that enables the use of AppAuth alongside Google Toolbox for Mac - Session Fetcher and Google APIs Client Library for Objective-C For REST.

    It provides an implementation of the GTMFetcherAuthorizationProtocol to authorize requests using AppAuth.

    Key benefits include:

    • Security & Usability: It uses the user's default browser for authorization, following modern OAuth best practices for native apps (RFC 8252).
    • Session Reuse: Users can leverage existing sessions in their default browser.
    • Migration Path: It offers compatibility methods for GTMOAuth2, allowing developers to migrate while preserving previously serialized authorizations so users do not need to re-authenticate.
  2. Understand the differences between GTMAppAuth and GTMOAuth2

    master

    If you are migrating from GTMOAuth2 to GTMAppAuth, be aware of these fundamental changes:

    • Authorization Method: GTMAppAuth uses the system browser for authorization requests, whereas GTMOAuth2 uses an embedded web-view.
    • Error Handling: GTMAppAuth does not use notifications. Instead, you must inspect the NSError in your callbacks.
      • If the error domain is OIDOAuthTokenErrorDomain, it is an authorization error; you should clear the authorization state and prompt the user to authorize again.
      • Other errors are generally considered transient and should be retried after a delay.
    • Serialization: GTMAppAuth uses a different data format that includes the client ID and a record of the authorization request. It is highly recommended to migrate to the new format to track different client IDs used for new and old grants.
  3. Migrate from file-based to data protection Keychain on macOS

    master

    Prior to version 5.0.0, GTMAppAuth used file-based Keychain storage. Current versions default to data protection Keychain storage. To migrate existing users:

    1. Attempt to retrieve the session using the new GTMKeychainStore (data protection).
    2. If not found, initialize a GTMKeychainStore with the GTMKeychainAttribute.useFileBasedKeychain attribute.
    3. Retrieve the session from the file-based store.
    4. If found, remove it from the file-based store and save it into the data protection store.
    // Attempt to retrieve from Data Protection Keychain
    GTMKeychainStore dataProtectionKeychainStore = [[GTMKeychainStore alloc] initWithItemName:kKeychainName];
    NSError *error;
    GTMAuthSession *authSession = [keychainStore retrieveAuthSessionWithError:&error];
    
    if (!authSession) {
      // Fallback to file-based
      GTMKeychainAttribute *fileBased = [GTMKeychainAttribute useFileBasedKeychain];
      NSSet *attributes = [NSSet setWithArray:@[fileBased]];
      GTMKeychainStore fileBasedKeychainStore = [[GTMKeychainStore alloc] initWithItemName:kExampleAuthorizerKey keychainAttributes:attributes];
      authSession =[fileBasedKeychainStore retrieveAuthSessionWithError:&error];
      
      if (authSession) {
        [fileBasedKeychainStore removeAuthSessionWithError:&error];
        [dataProtectionKeychainStore saveAuthSession:authSession error:&error];
      }
    }
  4. Run GTMAppAuth included samples

    master

    You can explore the project's functionality using the included sample apps in the Examples directory.

    • Swift Package Manager: Open Example-iOS.xcodeproj directly.
    • CocoaPods: Run pod install in the app's folder and open the resulting xcworkspace file.

    Note: You must follow the specific instructions in Example-iOS/README.md or Example-macOS/README.md to configure your own OAuth client ID for the examples to work.

  5. Handle OAuth redirect URIs on iOS and macOS

    master

    When the authorization flow completes, the platform redirects to your app via a custom URI scheme. You must pass this URL to the active authorization flow session.

    iOS

    Implement application:openURL:options: in your UIApplicationDelegate. Use resumeExternalUserAgentFlowWithURL: on your stored currentAuthorizationFlow object. If it returns YES, the flow has been resumed and you should return YES to the system.

    macOS

    Register for kAEGetURL events in applicationDidFinishLaunching:. In your handleGetURLEvent:withReplyEvent: handler, extract the URL string and pass it to resumeExternalUserAgentFlowWithURL:.

    // iOS Example
    - (BOOL)application:(UIApplication *)app
                openURL:(NSURL *)url
                options:(NSDictionary<NSString *, id> *)options {
      if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) {
        _currentAuthorizationFlow = nil;
        return YES;
      }
      return NO;
    }
  6. Install GTMAppAuth for iOS via Swift Package Manager or CocoaPods

    master

    You can integrate the iOS example project using either Swift Package Manager (SPM) or CocoaPods. Note that CocoaPods is in maintenance mode and SPM is strongly suggested.

    Swift Package Manager

    In the Example-iOS folder, run:

    open Example-iOS.xcodeproj

    CocoaPods

    1. In the Example-iOS folder, install the pods:
    $ pod install
    1. Open the generated workspace:
    $ open Example-iOSForPod.xcworkspace
    # For SPM
    open Example-iOS.xcodeproj
    
    # For CocoaPods
    $ pod install
    $ open Example-iOSForPod.xcworkspace
  7. Configure the macOS Example with your Client ID

    master

    The macOS example requires manual configuration of the Client ID and Redirect URI. Follow these three steps in order:

    1. Update Client ID: In GTMAppAuthExampleViewController.m, update the kClientID constant with your copied Client ID.
    2. Update Redirect URI: In GTMAppAuthExampleViewController.m, update kRedirectURI using the reverse DNS notation of your Client ID plus the path component :/oauthredirect.
      • Example: If your Client ID is YOUR_CLIENT.apps.googleusercontent.com, the kRedirectURI should be com.googleusercontent.apps.YOUR_CLIENT:/oauthredirect.
    3. Update Info.plist: Open Info.plist, expand the "URL types" (CFBundleURLTypes) section, and replace the existing identifier with your Client ID's reverse DNS notation (do not include the :/oauthredirect path component).
    // In GTMAppAuthExampleViewController.m
    
    // 1. Set your Client ID
    static NSString *const kClientID = @"YOUR_CLIENT_ID.apps.googleusercontent.com";
    
    // 2. Set your Redirect URI (Reverse DNS + path)
    static NSString *const kRedirectURI = @"com.googleusercontent.apps.YOUR_CLIENT_ID:/oauthredirect";
  8. Configure OAuth Client ID and Redirect URIs for iOS

    master

    To use the example, you must provide a valid Google OAuth client ID.

    1. Create a Client ID: Visit the Google Cloud Console, create a project, and create an OAuth client ID with the application type set to iOS.
    2. Identify Bundle ID: Use your project's Bundle ID. For the default example, it is com.example.GTMAppAuth.Example-iOS. You can change this via PRODUCT_BUNDLE_IDENTIFIER in your configuration.
    3. Calculate Redirect Values:
      • OIDC_CLIENT_ID: Your client ID (e.g., YOUR_CLIENT.apps.googleusercontent.com).
      • OIDC_REDIRECT_URI: The reverse DNS notation of your client ID with a path component. Example: If ID is YOUR_CLIENT.apps.googleusercontent.com, the URI is com.googleusercontent.apps.YOUR_CLIENT:/oauthredirect.
      • OIDC_REDIRECT_URI_SCHEME: The reverse DNS notation without the path component. Example: com.googleusercontent.apps.YOUR_CLIENT.
  9. Install the macOS Example via Swift Package Manager or CocoaPods

    master

    You can integrate the macOS example project using either Swift Package Manager or CocoaPods.

    Swift Package Manager: Open the project directly using the .xcodeproj file.

    CocoaPods:

    1. Install the required pods using pod install.
    2. Open the generated .xcworkspace file to ensure all dependencies are correctly linked.
    # Swift Package Manager
    open Example-macOS.xcodeproj
    
    # CocoaPods
    pod install
    open Example-macOSForPod.xcworkspace
  10. Authorize a user with GTMAppAuth

    master

    To initiate an authorization flow, follow these steps:

    1. Store the session: Maintain a property (typically in your UIApplicationDelegate) of type id<OIDExternalUserAgentSession> to track the in-progress flow.
    2. Store the auth state: Maintain a property (e.g., in your controller) of type GTMAuthSession to hold the resulting tokens.
    3. Execute the request: Use [OIDAuthState authStateByPresentingAuthorizationRequest:callback:]. This method automatically performs the OAuth token exchange and uses PKCE if supported by the server.

    In the callback, if authState is successful, initialize your GTMAuthSession with it.

    // 1. Build the request
    OIDAuthorizationRequest *request = [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                                                     clientId:kClientID
                                                                                 clientSecret:kClientSecret
                                                                                       scopes:@[OIDScopeOpenID, OIDScopeProfile]
                                                                                  redirectURL:redirectURI
                                                                                 responseType:OIDResponseTypeCode
                                                                       additionalParameters:nil];
    
    // 2. Perform request and store the flow in your app delegate
    self.appDelegate.currentAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request
        callback:^(OIDAuthState *_Nullable authState, NSError *_Nullable error) {
      if (authState) {
        // 3. Create GTMAuthSession from the state
        self.authSession = [[GTMAuthSession alloc] initWithAuthState:authState];
      } else {
        self.authSession = nil;
      }
    }];
  11. Make authorized API calls using GTMSessionFetcher

    master

    GTMAppAuth uses the Session Fetcher pattern to ensure HTTP requests are authorized with fresh tokens.

    1. Create a GTMSessionFetcherService.
    2. Assign your GTMAuthSession to the authorizer property of the service.
    3. Use the service to create a GTMSessionFetcher for specific URLs.
    4. When a request fails due to an expired token, check if the error domain is OIDOAuthTokenErrorDomain. If it is, the session is invalid and should be cleared.
    ```objc
    // Setup service with authorization
    GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
    fetcherService.authorizer = self.authSession;
    
    // Create fetcher and execute
    NSURL *userinfoEndpoint = [NSURL URLWithString:@