Git Product home page Git Product logo

gtmappauth's Introduction

Version Platform License tests

GTMAppAuth for Apple Platforms

GTMAppAuth enables you to use AppAuth with the Google Toolbox for Mac - Session Fetcher and Google APIs Client Library for Objective-C For REST libraries on iOS, macOS, tvOS, and watchOS by providing an implementation of GTMFetcherAuthorizationProtocol for authorizing requests with AppAuth.

GTMAppAuth is an alternative authorizer to GTMOAuth2 . The key differentiator is the use of the user's default browser for the authorization, which is more secure, more usable (the user's session can be reused) and follows modern OAuth best practices for native apps. Compatibility methods for GTMOAuth2 are offered allowing you to migrate from GTMOAuth2 to GTMAppAuth preserving previously serialized authorizations (so users shouldn't need to re-authenticate).

Setup

If you use CocoaPods, simply add:

pod 'GTMAppAuth'

To your Podfile and run pod install.

Usage

Configuration

To configure GTMAppAuth with the OAuth endpoints for Google, you can use the convenience method:

OIDServiceConfiguration *configuration = [GTMAuthSession configurationForGoogle];

Alternatively, you can configure GTMAppAuth by specifying the endpoints directly:

NSURL *authorizationEndpoint =
    [NSURL URLWithString:@"https://accounts.google.com/o/oauth2/v2/auth"];
NSURL *tokenEndpoint =
    [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"];

OIDServiceConfiguration *configuration =
    [[OIDServiceConfiguration alloc]
        initWithAuthorizationEndpoint:authorizationEndpoint
                        tokenEndpoint:tokenEndpoint];

// perform the auth request...

Or through discovery:

NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"];

[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
    completion:^(OIDServiceConfiguration *_Nullable configuration,
                 NSError *_Nullable error) {
  if (!configuration) {
    NSLog(@"Error retrieving discovery document: %@",
          [error localizedDescription]);
    return;
  }

  // perform the auth request...
}];

Authorizing

First, you need to have a way for your UIApplicationDelegate to continue the authorization flow session from the incoming redirect URI. Typically you could store the in-progress OIDAuthorizationFlowSession instance in a property:

// property of the app's UIApplicationDelegate
@property(nonatomic, nullable)
    id<OIDExternalUserAgentSession> currentAuthorizationFlow;

And in a location accessible by all controllers that need authorization, a property to store the authorization state:

// property of the containing class
@property(nonatomic, nullable) GTMAuthSession *authSession;

Then, initiate the authorization request. By using the authStateByPresentingAuthorizationRequest method, the OAuth token exchange will be performed automatically, and everything will be protected with PKCE (if the server supports it).

// builds authentication request
OIDAuthorizationRequest *request =
    [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                  clientId:kClientID
                                              clientSecret:kClientSecret
                                                    scopes:@[OIDScopeOpenID, OIDScopeProfile]
                                               redirectURL:redirectURI
                                              responseType:OIDResponseTypeCode
                                      additionalParameters:nil];
// performs authentication request
self.appDelegate.currentAuthorizationFlow =
    [OIDAuthState authStateByPresentingAuthorizationRequest:request
        callback:^(OIDAuthState *_Nullable authState,
                   NSError *_Nullable error) {
  if (authState) {
    // Creates a GTMAuthSession from the OIDAuthState.
    self.authSession = [[GTMAuthSession alloc] initWithAuthState:authState];
    NSLog(@"Got authorization tokens. Access token: %@",
          authState.lastTokenResponse.accessToken);
  } else {
    NSLog(@"Authorization error: %@", [error localizedDescription]);
    self.authSession = nil;
  }
}];

Handling the Redirect

The authorization response URL is returned to the app via the platform-specific application delegate method, so you need to pipe this through to the current authorization session (created in the previous session).

macOS Custom URI Scheme Redirect Example

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  // Other app initialization code ...

  // Register for GetURL events.
  NSAppleEventManager *appleEventManager =
      [NSAppleEventManager sharedAppleEventManager];
  [appleEventManager setEventHandler:self
                         andSelector:@selector(handleGetURLEvent:withReplyEvent:)
                       forEventClass:kInternetEventClass
                          andEventID:kAEGetURL];
}

- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
           withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
  NSString *URLString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
  NSURL *URL = [NSURL URLWithString:URLString];
  [_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:URL];
}

iOS Custom URI Scheme Redirect Example

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
  // Sends the URL to the current authorization flow (if any) which will
  // process it if it relates to an authorization response.
  if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) {
    _currentAuthorizationFlow = nil;
    return YES;
  }

  // Your additional URL handling (if any) goes here.

  return NO;
}

Making API Calls

The goal of GTMAppAuth is to enable you to authorize HTTP requests with fresh tokens following the Session Fetcher pattern, which you can do like so:

// Creates a GTMSessionFetcherService with the authorization.
// Normally you would save this service object and re-use it for all REST API calls.
GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
fetcherService.authorizer = self.authSession;

// Creates a fetcher for the API call.
NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v3/userinfo"];
GTMSessionFetcher *fetcher = [fetcherService fetcherWithURL:userinfoEndpoint];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
  // Checks for an error.
  if (error) {
    // OIDOAuthTokenErrorDomain indicates an issue with the authorization.
    if ([error.domain isEqual:OIDOAuthTokenErrorDomain]) {
      self.authSession = nil;
      NSLog(@"Authorization error during token refresh, clearing state. %@",
            error);
    // Other errors are assumed transient.
    } else {
      NSLog(@"Transient error during token refresh. %@", error);
    }
    return;
  }

  // Parses the JSON response.
  NSError *jsonError = nil;
  id jsonDictionaryOrArray =
      [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];

  // JSON error.
  if (jsonError) {
    NSLog(@"JSON decoding error %@", jsonError);
    return;
  }

  // Success response!
  NSLog(@"Success: %@", jsonDictionaryOrArray);
}];

Saving to the Keychain

You can easily save GTMAuthSession instances to the Keychain using the GTMKeychainStore class.

// Create a GIDKeychainStore instance, intializing it with the Keychain item name `kKeychainItemName`
// which will be used when saving, retrieving, and removing `GTMAuthSession` instances.
GIDKeychainStore *keychainStore = [[GIDKeychainStore alloc] initWithItemName:kKeychainItemName];
    
NSError *error;

// Save to the Keychain
[keychainStore saveAuthSession:self.authSession error:&error];
if (error) {
  // Handle error
}

// Retrieve from the Keychain
self.authSession = [keychainStore retrieveAuthSessionWithError:&error];
if (error) {
  // Handle error
}

// Remove from the Keychain
[keychainStore removeAuthSessionWithError:&error];
if (error) {
  // Handle error
}

Keychain Storage

With GTMKeychainStore, by default, GTMAuthSession instances are stored using Keychain items of the kSecClassGenericPassword class with a kSecAttrAccount value of "OAuth" and a developer supplied value for kSecAttrService. For this use of generic password items, the combination of account and service values acts as the primary key of the Keychain items. The kSecAttrAccessible key is set to kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly in order to allow background access after initial device unlock following a restart. A keyed archive representation of the relevant GTMAuthSession instance is supplied as the value for kSecValueData and this is encrypted and stored by Keychain Services.

For macOS, two Keychain storage options are available: the traditional file-based Keychain storage which uses access control lists and the more modern data protection keychain storage which uses Keychain access control groups. By default, GTMAppAuth uses the file-based Keychain storage on macOS. You may opt into using data protection keychain storage by including the GTMKeychainAttribute.useDataProtectionKeychain attribute in the keychainAttributes parameter of initWithItemName:keychainAttributes: when initializing GTMKeychainStore. Note that Keychain items stored via one storage type will not be available via the other and macOS apps that choose to use the data protection Keychain will need to be signed in order for Keychain operations to succeed.

Implementing Your Own Storage

If you'd like to use a backing store other than the Keychain to save your GTMAuthSession instances, you can create your own GTMAuthSessionStore conformance. Use GTMKeychainStore as an example of how to do this.

GTMOAuth2 Compatibility

To assist the migration from GTMOAuth2 to GTMAppAuth, GTMOAuth2-compatible Keychain methods are provided in GTMKeychainStore.

GTMKeychainStore keychainStore = [[GTMKeychainStore alloc] initWithItemName:kKeychainItemName];

// Retrieve from the Keychain
NSError *error;
GTMAuthSession *authSession =
    [keychainStore retrieveAuthSessionForGoogleInGTMOAuth2FormatWithClientID:clientID
                                                                clientSecret:clientSecret
                                                                       error:&error];

// Remove from the Keychain
[keychainStore removeAuthSessionWithError:&error];

You can also save to GTMOAuth2 format, though this is discouraged (you should save in GTMAppAuth format as described above).

// Save to the Keychain
[keychainStore saveWithGTMOAuth2FormatForAuthSession:authSession error:&error];

Included Samples

Try out one of the included sample apps under Examples. In the apps folder run pod install, then open the resulting xcworkspace file.

Be sure to follow the instructions in Example-iOS/README.md or Example-macOS/README.md to configure your own OAuth client ID for use with the example.

Differences with GTMOAuth2

Authorization Method

GTMAppAuth uses the browser to present the authorization request, while GTMOAuth2 uses an embedded web-view. Migrating to GTMAppAuth will require you to change how you authorize the user. Follow the instructions above to get the authorization. You can then create a GTMAuthSession object with its initWithAuthState: initializer. Once you have a GTMAuthSession you can continue to make REST calls as before.

Error Handling

GTMAppAuth's error handling is also different. There are no notifications, instead you need to inspect NSError in the callback. If the error domain is OIDOAuthTokenErrorDomain, it indicates an authorization error, you should clear your authorization state and consider prompting the user to authorize again. Other errors are generally considered transient, meaning that you should retry the request after a delay.

Serialization

The serialization format is different between GTMOAuth2 and GTMAppAuth, though we have methods to help you migrate from one to the other without losing any data.

Migrating from GTMOAuth2

OAuth Client Registration

Typically, GTMOAuth2 clients are registered with Google as type "Other". Instead, Apple clients should be registered with the type "iOS".

If you're migrating an Apple client in the same project as your existing client, register a new iOS client to be used with GTMAppAuth.

Changing your Authorization Flows

Both GTMOAuth2 and GTMAppAuth support the GTMFetcherAuthorizationProtocol allowing you to use the authorization with the session fetcher. Where you previously had a property like GTMOAuth2Authentication *authorization change the type to reference the protocol instead, i.e.: id<GTMFetcherAuthorizationProtocol> authorization. This allows you to switch the authorization implementation under the hood to GTMAppAuth.

Then, follow the instructions above to replace authorization request (where you ask the user to grant access) with the GTMAppAuth approach. If you created a new OAuth client, use that for these requests.

Serialization & Migrating Existing Grants

GTMAppAuth has a new data format and APIs for serialization. Unlike GTMOAuth2, GTMAppAuth serializes the configuration and history of the authorization, including the client id, and a record of the authorization request that resulted in the authorization grant.

The client ID used for GTMAppAuth is different to the one used for GTMOAuth2. In order to keep track of the different client ids used for new and old grants, it's recommended to migrate to the new serialization format, which will store that for you. GTMOAuth2-compatible serialization is also offered, but not fully supported.

Change how you serialize your authorization object by using GTMAuthSession and GTMKeychainStore as follows:

// Create an auth session from AppAuth's auth state object
GTMAuthSession *authSession = [[GTMAuthSession alloc] initWithAuthState:authState];

// Create a keychain store
GTMKeychainStore keychainStore = [[GTMKeychainStore alloc] initWithItemName:kNewKeychainName];

// Serialize to Keychain
NSError *error;
[keychainStore saveAuthSession:authSession error:&error];

Be sure to use a new name for the keychain. Don't reuse your old one!

For deserializing, we can preserve all existing grants (so users who authorized your app in GTMOAuth2 don't have to authorize it again). Remember that when deserializing the old data you need to use your old keychain name, and the old client id and client secret (if those changed), and that when serializing to the new format, use the new keychain name. Once again, pay particular care to use the old details when deserializing the GTMOAuth2 keychain, and the new details for all other GTMAppAuth calls.

Keychain migration example:

// Create a keychain store
GTMKeychainStore keychainStore = [[GTMKeychainStore alloc] initWithItemName:kNewKeychainName];

// Attempt to deserialize from Keychain in GTMAppAuth format.
NSError *error;
GTMAuthSesion *authSession =
    [keychainStore retrieveAuthSessionWithError:&error];

// If no data found in the new format, try to deserialize data from GTMOAuth2
if (!authSession) {
  // Tries to load the data serialized by GTMOAuth2 using old keychain name.
  // If you created a new client id, be sure to use the *previous* client id and secret here.
  GTMKeychainStore oldKeychainStore = [[GTMKeychainStore alloc] initWithItemName:kPreviousKeychainName];
  authSession =
      [oldKeychainStore retrieveAuthSessionInGTMOAuth2FormatWithClientID:kPreviousClientID
                                                            clientSecret:kPreviousClientSecret
                                                                   error:&error];
  if (authSession) {
    // Remove previously stored GTMOAuth2-formatted data.
    [oldKeychainStore removeAuthSessionWithError:&error];
    // Serialize to Keychain in GTMAppAuth format.
    [keychainStore saveAuthSession:authSession error:&error];
  }
}

gtmappauth's People

Contributors

alex-4-git avatar dependabot[bot] avatar karimhm avatar maxgoedjen avatar mdmathias avatar mwyman avatar niallkennedy avatar paulb777 avatar petea avatar protocol86 avatar ryanwilson avatar thomasvl avatar williamdenniss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gtmappauth's Issues

Crashed calling any method after login

*** Terminating app due to uncaught exception 'Attempted to create a token refresh request from a token response with no refresh token.', reason: 'Attempted to create a token refresh request from a token response with no refresh token.'
When run the Drive Sample, it crashed by calling fetchFileList ([service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket,
GTLRDrive_FileList *fileList,
NSError *callbackError) { ) :

Swift Examples?

Will there be any Swift example projects?

The Google tutorials are outdated, using a UIWebView which can no longer be used

Support Carthage.

Supporting Carthage would go a long way for making this Library easier to use in Swift and iOS in general. Many project might not allow cocoapods.

How is the refresh token used?

The way I understood this guide is that you receive a refresh token and an access token. The access token is only valid for a limited time and when it expires you have to use the refresh token to get a new access token (instead of requiring the user to sign in again). How is this handled in this library and in combination with the Google API Objective-C library? The readme doesn't mention any token refresh request. Any clarification would be appreciated :)

Does not build on Xcode 8.3

<unknown>:0: error: a declaration cannot be both 'final' and 'dynamic' <unknown>:0: error: a declaration cannot be both 'final' and 'dynamic'

    cd /Users/project/project
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c /Users/project/project/drive-down/GoogleFileListTableViewController.swift -primary-file /Users/project/project/drive-down/AppDelegate.swift -target x86_64-apple-ios7.1 -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk -I /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Products/Debug-iphonesimulator -F /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Products/Debug-iphonesimulator -g -module-cache-path /Users/project/Library/Developer/Xcode/DerivedData/ModuleCache -serialize-debugging-options -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/drive-down-generated-files.hmap -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/drive-down-own-target-headers.hmap -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/drive-down-all-target-headers.hmap -Xcc -iquote -Xcc /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/drive-down-project-headers.hmap -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Products/Debug-iphonesimulator/include -Xcc -I/Users/project/project/Pods/Headers/Public -Xcc -I/Users/project/project/Pods/Headers/Public/AppAuth -Xcc -I/Users/project/project/Pods/Headers/Public/GTMAppAuth -Xcc -I/Users/project/project/Pods/Headers/Public/GTMSessionFetcher -Xcc -I/Users/project/project/Pods/Headers/Public/GoogleAPIClient -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/DerivedSources/x86_64 -Xcc -I/Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/DerivedSources -Xcc -DDEBUG=1 -Xcc -DCOCOAPODS=1 -Xcc -DDEBUG=1 -Xcc -DGTL_USE_FRAMEWORK_IMPORTS=1 -Xcc -working-directory/Users/project/project -emit-module-doc-path /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate~partial.swiftdoc -serialize-diagnostics-path /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate.dia -import-objc-header /Users/project/project-Bridging-Header.h -Onone -module-name drive_down -emit-module-path /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate~partial.swiftmodule -emit-dependencies-path /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate.d -emit-reference-dependencies-path /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate.swiftdeps -o /Users/project/Library/Developer/Xcode/DerivedData/drive-down-dekhohhcqbrgledxhnhfpvavzzzv/Build/Intermediates/drive-down.build/Debug-iphonesimulator/drive-down.build/Objects-normal/x86_64/AppDelegate.o

/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:19:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:19:
#import "OIDAuthState.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDAuthState.h:131:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:23:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:23:
#import "OIDAuthorizationResponse.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDAuthorizationResponse.h:95:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:24:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:24:
#import "OIDAuthorizationService.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDAuthorizationService.h:77:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:33:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:33:
#import "OIDServiceDiscovery.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDServiceDiscovery.h:319:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:34:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:34:
#import "OIDTokenRequest.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDTokenRequest.h:102:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:35:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:35:
#import "OIDTokenResponse.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDTokenResponse.h:87:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
/Users/project/project-Bridging-Header.h:6:9: note: in file included from /Users/project/project-Bridging-Header.h:6:
#import "AppAuth.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:43:9: note: in file included from /Users/project/project/Pods/Headers/Public/AppAuth/AppAuth.h:43:
#import "OIDAuthorizationUICoordinatorIOS.h"
        ^
/Users/project/project/Pods/Headers/Public/AppAuth/OIDAuthorizationUICoordinatorIOS.h:55:1: warning: conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'
- (nullable instancetype)init NS_UNAVAILABLE;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.3.sdk/usr/include/objc/NSObject.h:60:1: note: previous declaration is here
- (instancetype)init
^
<unknown>:0: error: a declaration cannot be both 'final' and 'dynamic'
<unknown>:0: error: a declaration cannot be both 'final' and 'dynamic'

Podspec:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

target 'project' do
    pod 'GoogleAPIClient/Drive', '~> 1.0'
    pod 'GTMAppAuth'
    pod 'GTMSessionFetcher'
end

Briding Header:

#import "GTLDrive.h"
#import "AppAuth.h"
#import "GTMAppAuth.h"

Additionally why are there no good guides for building these SDK's as frameworks? Not every project can use Cocoapods.

Terminating app due to uncaught exception 'Attempt to call unavailable initializer.', reason: 'Called: init Designated Initializer:initWithAuthState:'

I am using google drive sdk and I have implemented GTMAppAuth for login in my swift project.

In AppDelegate.swift:

weak var currentAuthorizationFlow: OIDAuthorizationFlowSession?
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        if (currentAuthorizationFlow?.resumeAuthorizationFlow(with: url))! {
            currentAuthorizationFlow = nil
            return true
        }
        // Your additional URL handling (if any) goes here.
        return false
    }

In ViewController.swift:

//MARK:- Login Tapped
    @IBAction func login_Tapped(_ sender: RBButton) {
        let issuer = URL.init(string: kIssuer)
        let redirectUrl = URL.init(string: kRedirectURI)
        
        OIDAuthorizationService.discoverConfiguration(forIssuer: issuer!) { (configuration, error) in
            if !(configuration != nil) {
                print("Error retrieving discovery document: \(error?.localizedDescription)")
                //self.setGTMAuthorization(authorization: nil)
                return
            }
            print("Got Configuration: \(configuration)")
            
            //authentication request
            let request = OIDAuthorizationRequest.init(configuration: configuration!, clientId: kClientID, scopes: [OIDScopeOpenID, OIDScopeProfile], redirectURL: redirectUrl!, responseType: OIDResponseTypeCode, additionalParameters: nil)
            
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            print("Initiating authorization request with scope: \(request.scope)")
            
            appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, error) in
                if (authState != nil) {
                    let authorization = GTMAppAuthFetcherAuthorization.init(authState: authState!)
                    self.setGTMAuthorization(authorization: authorization)
                    print("Got authorization access token: \(authState?.lastTokenResponse?.accessToken)")
                } else {
                    //self.setGTMAuthorization(authorization: nil)
                    print("Authorization error: \(error?.localizedDescription)")
                }
            })
        }
    }
    
    func setGTMAuthorization(authorization: GTMAppAuthFetcherAuthorization) {
        if appAuthorization.isEqual(authorization) {
            return
        }
        appAuthorization = authorization
        self.stateChanged()
    }
    
    func stateChanged() {
        self.saveState()
        self.updateUI()
    }
    
    func saveState() {
        if appAuthorization.canAuthorize() {
            GTMAppAuthFetcherAuthorization.save(appAuthorization, toKeychainForName: kExampleAuthorizerKey)
        } else {
            GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: kExampleAuthorizerKey)
        }
    }

But app is crashing(as title says) while view load. BTW my viewDidLoad() is empty.

I have put exception breakpoint and it stops here:
GTM_UNAVAILABLE_USE_INITIALIZER(@selector(initWithAuthState:));

Please help regarding this issue.

How to get refreshToken [To avoid user login everytime in the iOS app].

Hi,
I made successful authentication, now user can able to login in my app. But the problem is once user loggs into the app no need to login again that is my requirement, so for this purpose i went through the document which is provided then i understood that using refreshToken i can get accessToken. But in the document refreshToken details are not mentioned.

Here is the code i used to get refreshToken----->
let auth = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: keyChainForAccount)

if auth?.authState.refreshToken == nil {
// some perform segue methods i will call here
}
else{
auth?.authorizeRequest(nil, delegate: self, didFinish: #selector(LaunchViewController.auth(_:finishedRefreshWithFetcher:error:)))
}

func auth(_ auth: GTMAppAuthFetcherAuthorization, finishedRefreshWithFetcher fetcher: GTMSessionFetcher, error: NSError) {
//we come here if and only if we already have an account
if error._userInfo != nil {
self.displayDebugAlert("Error At line 482 with (error.userInfo)")
} else {
loadAuth(auth)
}
}

func loadAuth(_ auth: GTMAppAuthFetcherAuthorization) {
#if DEBUG
NSLog("fetch auth from google completed")
#endif
loadAccoutWithUserName(auth.userEmail!, password: nil, hostname: "imap.gmail.com", oauth2Token:"(String(describing: auth.authState.lastTokenResponse!.accessToken!))") . ////This line will takes me to the mailcore2 For fetching emails
}

How can i get accessToken Using refreshToken in GTMAppAuth Authorisation..........thanks in advance.

how to obtain a JWT token (id_token)?

In the previous version of the library (gtm-oauth2), there was a way to obtain the JWT token from an authorization object using the id_token parameter. I use JWTs for cross-device authentication, so I want to get access to the token and authenticate my requests myself.

    // else, the real version
    if (self.currAuth != NULL) {
        if (self.currAuth.canAuthorize) {
            return [self.currAuth.parameters valueForKey:@"id_token"];
        }
    }

I don't see a way to do that with this replacement library. In particular, I don't see a parameters property for the GTMAppAuthFetcherAuthorization, and I don't see the source code for the OIDAuthState object, which is where the rest of the tokens appear to live.

Compatibility with iOS 10?

I've used the code in example and got it working perfectly in iOS 9.3, invoking a normal browser window, navigates to google to sign in / give permissions, then return to app and consume tokens etc.

I've experienced with the exact same code, in iOS 10, when it invokes a normal browser window, it doesn't navigate to google to sign in, just sits with a blank window. nothing happens. its very strange.

screen shot 2017-01-15 at 10 57 38 pm

Any ideas?

GTMAppAuthDriveExampleiOS/Pods/AppAuth/Source/iOS/OIDAuthorizationUICoordinatorIOS.h:58:12: Parameter 'presentingViewController' not found in the function declaration

I just following the steps:

target 'GTMAppAuth Drive Example iOS' do
  platform :ios, '8.0'

  pod 'GTMAppAuth'
  pod 'GoogleAPIClientForREST/Drive'
end

$ pod install
$ open Example-iOS.xcworkspace
Copy the client ID to the clipboard from google console.
In GTMAppAuthExampleViewController.m update kClientID with new client id.

Finally, open Info.plist and fully expand "URL types" (a.k.a. "CFBundleURLTypes") and replace com.googleusercontent.apps.YOUR_CLIENT with the reverse DNS notation form of your client id (not including the :/oauthredirect path component).

But
Why error in the comment ?
Xcode 8.3.1
2017-04-17 12 15 56

How to get oauthredirect to create kRedirectURI

kRedirectURI is created from fomat :
kRedirectURI = @"com.googleusercontent.apps.YOUR_CLIENT:/oauthredirect";

but, What is oauthredirect ? and how to get it ?
That display error : redirect_uri_mismatch

AppAuth: Parameter 'presentingViewController' not found in the function declaration

Got a build error in the file OIDAuthorizationUICoordinatorIOS.h:

`/*! @brief The designated initializer.
@param presentingViewController The view controller from which to present the
\SFSafariViewController.
*/

  • (nullable instancetype)initWithPresentingViewController:(UIViewController *)parentViewController
    NS_DESIGNATED_INITIALIZER;
    `

The parameter 'presentingViewController' seems wrong and AppAuth has already fixed it.

Using gmail account from cache

Since it uses SFSafariViewController, is it possible to use the earlier signed-in Gmail account from cache?

We have made an app and integrated GTMAppAuth library. We have a scenario, where user logs in for the first time via Gmail app and use the app. Now user deletes the app and re-install. Now when user tries to login again, it never uses the logged-in session and it always asks user to login again.

Is there any workaround where we may ask to use login from cache if available?

Is there a way to add more scopes to the authorization

Using the below code we provide the scopes. In the below code I added scope for mail. Is it possible for me to add more scopes to the current authState. Say I want to add calendar scope later to the existing authState. How should I do it.

[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kClientID
clientSecret:kClientSecret
scopes:@[OIDScopeOpenID, OIDScopeProfile, @"https://mail.google.com/"]
redirectURL:redirectURI
responseType:OIDResponseTypeCode
additionalParameters:nil];

Please update Pod repository

Currently, pod install GTMAppAuth will only install version 0.5.0 which yields compilation errors if used together with "AppAuth" and Swift 3.

[Suggestion] Add a manual method to refresh the token for non-HTTP protocol usage

Background: for non-HTTP protocols that use GTMAppAuth, e.g. SMTP->GMail, the GTMAppAuth method authorizeRequest: is not called, because it requires an HTTP-request as input. Instead, the access token is accessed directly via .authState.lastTokenResponse.accessToken and used in whatever payload (ie. SMTP).

Issue: the only way I see the accessToken getting refreshed using the refreshToken is if authorizeRequest: is called. This necessitates calling authorizeRequest: with a dummy HTTP-based NSMutableURLRequest, which is non-obvious (see #44 for at least one instance of someone getting snarled by this other than me), and feels weird.

Suggestion: either add a new method like refreshAccessTokenIfNeeded which will either no-op or refresh the accessToken if needed

Thanks.

Podspec not pushed to trunk

Hey there,

after getting no response on my PR the repo apparently got updated by Phillip Zsika. He bumped the podspec to 0.6.0 but seemed to have forgotten to push the spec to the cocoapods repo so it can be actually discovered and used.

Can you please update the pod repo in a timely manner so people can start using this library again?!

Jonas

AppAuth upgrade problem

The latest AppAuth version had change to 0.90.0, but the version in podspec is still limit to 0.9.x. Need change podspec define and macOS support.

Migrate error [GTMAppAuthFetcherAuthorization authorizationFromKeychainForName:kAuthorizerKey] Exception

Compatibility methods for GTMOAuth2 are offered allowing you to migrate from GTMOAuth2 to GTMAppAuth preserving previously serialized authorizations (so users shouldn't need to re-authenticate).

But after upgrade the app always break down~~~
If I remove the APP and re-install the app will work well.

- (void)viewDidLoad{
  [super viewDidLoad];
  NSString* kAuthorizerKey=@"key";
  self.driveService = [[GTLRDriveService alloc] init];
  self.driveService.authorizer = [GTMAppAuthFetcherAuthorization authorizationFromKeychainForName:kAuthorizerKey];
...
}
Fatal Exception: NSInvalidArgumentException
0  CoreFoundation                 0x252a045f __exceptionPreprocess
1  libobjc.A.dylib                0x33136c8b objc_exception_throw
2  CoreFoundation                 0x252a03a5 -[NSException initWithCoder:]
3  Foundation                     0x25f62ca5 -[NSKeyedUnarchiver initForReadingWithData:]
4  Foundation                     0x25f62269 +[NSKeyedUnarchiver unarchiveObjectWithData:]
5                                 0x198abb +[GTMAppAuthFetcherAuthorization(Keychain) authorizationFromKeychainForName:] (GTMAppAuthFetcherAuthorization+Keychain.m:31)
6                                 0x13250b -[FirstViewController viewDidLoad] (FirstViewController.m:156)

After Successfull google authorisation(GTMAppAuth) sometimes google.co.in page is loading

I have done successfull google authentication with google GTMAppAuth library. Here user can able to login but sometimes not in everytime google.co.in search page is loading. I made authoration with the below code. once user enters into his username and password it should ask allow access to the user after it will come back to the application. The problem is loading google search page on sometimes.

func startgetauth(){

//need this potatoe
let issuer = URL(string: kIssuer)!
let redirectURI = URL(string: kRedirectURI)!

    // discovers endpoints
    OIDAuthorizationService.discoverConfiguration(forIssuer: issuer, completion: {(_ configuration: OIDServiceConfiguration?, _ error: Error?) -> Void in
        if configuration == nil {

            return
        }

        // builds authentication request
        let scopes = [OIDScopeOpenID, OIDScopeProfile, OIDScopeEmail, "https://mail.google.com/", OIDScopeAddress, OIDScopePhone ]

        let request = OIDAuthorizationRequest(configuration: configuration!, clientId: self.kClientID, clientSecret:nil, scopes: scopes, redirectURL: redirectURI, responseType: OIDResponseTypeCode, additionalParameters: nil)
        
        // performs authentication request

        self.appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self, callback: {(_ authState: OIDAuthState?, _ error: Error?) -> Void in
            if authState != nil {
                print("access Token :\(String(describing: authState!.lastTokenResponse!.accessToken!))")
                self.auth = GTMAppAuthFetcherAuthorization(authState: authState!)
                print("Authorisation \(self.auth!)")
                self.doneFetchingAuth(self.auth!)
                GTMAppAuthFetcherAuthorization.save(self.auth!, toKeychainForName: self.keyChainForAccount)
                print("SAVED KEYCHAIN DETAILKS: \(self.keyChainForAccount)")
                
           //     GTMAppAuthFetcherAuthorization.auth.redirectURI = "http://localhost//mailDoughFull"
                		#if FREE
               // 				GTMAppAuthFetcherAuthorization.auth.redirectURI = "http://localhost//mailCleanerFree"
                			#endif
            }
            else {
                print("Authorization error: " + (error?.localizedDescription.description)!)

            }
        })
    })
    
    		oauthViewControllerShown = true

}
BUG Video.zip

@WilliamDenniss @dberlin @willnorris @wkiefer @cdibona

urn:ietf:wg:oauth:2.0:oob not supported

I realise the library is optimised for Google APIs, however I have successfully used the previous one with other services that support OAuth2 (namely Outlook and Yahoo). Since GTMAppAuth is based on top of AppAuth, it seems the browser title redirect method is not supported. Outlook supports native apps however it does not allow us to specify a custom redirect URL.

Will the browser title method be supported at all?

how can I add scopes for Google Drive REST API

Google Drive scopes are described in this link: https://developers.google.com/drive/v2/web/scopes, and I tried to add one of scopes into GTMAppAuth iOS demo code, but it does not work (Google Drive related authorization does not come)

static NSString *const GoogleDriveScopeProfile = @"https://www.googleapis.com/auth/drive";

OIDAuthorizationRequest *request =
        [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                      clientId:kClientID
                                                        scopes:@[OIDScopeOpenID, OIDScopeProfile,GoogleDriveScopeProfile]
                                                   redirectURL:redirectURI
                                                  responseType:OIDResponseTypeCode
                                          additionalParameters:nil];

kGTLRAuthScopeYouTube and unlinked accounts

In the previous version, before AppAuth, when a user's account is identified as "unlinked" (i.e. they don't have a YouTube channel) we'd open a webview to:
https://m.youtube.com/create_channel?chromeless=1&next=/channel_creation_done

And then monitor the webview's load requests for "channel_creation_done" per these API instructions:
https://developers.google.com/youtube/create-channel

Is there a recommended way to do this with the new AppAuth? We can send them to an SFSafariViewController, but there's no way to know when they are done/successful.

This is probably a complicated design issue, but it seems really odd that when we authorize with just the kGTLRAuthScopeYouTube scope AND the user doesn't have a YouTube account, they are still asked if it's ok for us to "Manage your YouTube account".

User email is nil in OIDAuthState after authorization

Here is code from your example

// builds authentication request
OIDAuthorizationRequest *request =
    [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                  clientId:kClientID
                                              clientSecret:kClientSecret
                                                    scopes:@[OIDScopeOpenID, OIDScopeProfile]
                                               redirectURL:redirectURI
                                              responseType:OIDResponseTypeCode
                                      additionalParameters:nil];
// performs authentication request
self.appDelegate.currentAuthorizationFlow =
    [OIDAuthState authStateByPresentingAuthorizationRequest:request
        callback:^(OIDAuthState *_Nullable authState,
                   NSError *_Nullable error) {
  if (authState) {
    // Creates the GTMAppAuthFetcherAuthorization from the OIDAuthState.
    GTMAppAuthFetcherAuthorization *authorization =
        [[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];

    self.authorization = authorization;
    NSLog(@"Got authorization tokens. Access token: %@",
          authState.lastTokenResponse.accessToken);
  } else {
    NSLog(@"Authorization error: %@", [error localizedDescription]);
    self.authorization = nil;
  }
}];

Why GTMAppAuthFetcherAuthorization initialized by authState have nil userEmail? I used it and it was there. Even after using userInfo endpoint in your example there isn't user email. How can I get user email after authorization?

Podspec needs a newer AppAuth

The current Podspec requires AppAuth 0.9.0:

s.dependency 'AppAuth', '~> 0.9.0'

This can lead to compile errors due to the availability of SFSafariViewController. I first encountered this with Xcode 9.2 but I'm not certain if that's the cause. This is fixed in AppAuth version 0.9.1.

For example, 0.9.0 includes this snippet:

@implementation OIDAuthorizationUICoordinatorIOS {
  UIViewController *_presentingViewController;

  BOOL _authorizationFlowInProgress;
  __weak id<OIDAuthorizationFlowSession> _session;
  __weak SFSafariViewController *_safariVC;
}

Compilation fails with an error reading 'SFSafariViewController' is partial: introduced in iOS 9.0. This happens even if the app's deployment target is iOS 10.

In 0.9.1 this is fixed as follows:

@implementation OIDAuthorizationUICoordinatorIOS {
  UIViewController *_presentingViewController;

  BOOL _authorizationFlowInProgress;
  __weak id<OIDAuthorizationFlowSession> _session;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
  __weak SFSafariViewController *_safariVC;
  SFAuthenticationSession *_authenticationVC;
#pragma clang diagnostic pop
}

Terminating app due to uncaught exception 'NSInternalInconsistencyException'

Hi ,
I have replaced lclient in all places with proper change in Kclientid and redirect uri. example-IOS is reunning successful but after that this example is showing exception. Even though i have tried many times. do i need to get any licence to run this example ios project. if anyone knows please help me.

Thanks in advance

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Update kClientID with your own client ID. Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Example/README.md'

any plans to converge this with Google Sign-In?

Unless I'm misunderstanding something, this seems to solve the same problem as the Google Sign-In library. Can you provide any guidance as to when this or that is a better choice than the other? Will the two eventually be converged?

invalid_client after migrating from GTMOAuth2 to GTMAppAuth

I've successfully followed and migrated from the old format to the new one (and generated a new client ID for the new format). However, I am now seeing this invalid_client in the console with description Unauthorized. I suspect this may have something to do with the changed client ID, however the guide recommends we use a new client ID.

What should I do in this case?

Swift Port for this?

Hi
Is there a swift port for the example project in the roadmap? - or this project going to be deprecated in favour of the Google Sign In SDK?
This seems very good and simple to use.

Can i get authorization with a Service Account?

I need to access a Google sheet for update from an iOS App without user intervention (neither for sign-in).

Reading the Google API documentation, i know i must use a Service Account to get authorization. I already generate the service account, but i don't know how to ask for authorization. Can anyone please help me?

Thanks,

Fernando

Generate server auth code from macOS app

Hi,

I have successfully setup the flow for a user to login to his Gmail account from my macOS app.
The issue is, it is not the app that will access the user's data, but a server. The app should just takes care of the Gmail login and generate a server authorisation code that can then be sent to the server.

Is there any way, similarly to the iOS Google SDK, to specify the client ID of the server, so that the authorisation code generated can be used by the server ?

Thanks.

GTMAppAuth flow breaks if using Google Chrome

Steps to reproduce (Mac):

  1. Ensure default browser is Google Chrome
  2. Begin auth flow from app
  3. Sign-in to reach the consent page in Chrome
  4. Click Allow
  5. If opening native callback url via Chrome for the first time, an "Open App" dialogue is shown.
  6. Wait until auth page navigates away to google.com and click "Open App"
  7. Notice callback URL is not called and flow breaks.

Issue does not happen on Firefox/Safari. I figure this is more of a Chromium bug than an GTMAppAuth bug, but there needs to be a more graceful way of handling this on the client end.

Error 500 when re-authenticating

Hi,

I have a working example of using this library to receive a Gmail token. Initial flow works really well.
However, if I try to re-authenticate a user, and the app is still indicated as authorized in the user's security settings, I always get an error 500 page from Google.

Error received in Safari is attached.
Can someone tell me what I could have done wrong ?

Here is the code:
`NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"];
[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {

if (!configuration) {
  [self logMessage:@"Error retrieving discovery document: %@", [error localizedDescription]];
  [self setAuthorization:nil];
  return;
}

[self logMessage:@"Got configuration: %@", configuration];

// builds authentication request
NSArray *scopes = @[
					OIDScopeOpenID,
					@"email",
					@"https://www.googleapis.com/auth/userinfo.profile",
					@"https://mail.google.com",
					@"https://www.google.com/m8/feeds",
					];
OIDAuthorizationRequest *request =
    [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                  clientId:kClientID
                                              clientSecret:kClientSecret
                                                    scopes:scopes
                                               redirectURL:redirectURI
                                              responseType:OIDResponseTypeCode
                                      additionalParameters:nil];

// performs authentication request
self.appDelegate.currentAuthorizationFlow =
    [OIDAuthState authStateByPresentingAuthorizationRequest:request
                        callback:^(OIDAuthState *_Nullable authState,
                                   NSError *_Nullable error) {
  if (authState) {
    GTMAppAuthFetcherAuthorization *authorization =
        [[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
    [self setAuthorization:authorization];
    [self logMessage:@"Got authorization tokens. Access token: %@",
                     authState.lastTokenResponse.accessToken];
  } else {
    [self setAuthorization:nil];
    [self logMessage:@"Authorization error: %@", [error localizedDescription]];
  }
}];

}];`

screen shot 2017-02-13 at 14 04 41

Terminating app due to uncaught exception 'NSInternalInconsistencyException

Hi ,
I have replaced lclient in all places with proper change in Kclientid and redirect uri. example-IOS is reunning successful but after that this example is showing exception. Even though i have tried many times. do i need to get any licence to run this example ios project. if anyone knows please help me.

Thanks in advance

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Update kClientID with your own client ID. Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Example/README.md'

iOS API Calling Error

Hello,

I am trying to live broadcast from my custom app and using liveBroadcasts api. But I am getting error each time. Kindly have a look on my code snippet and correct.

    GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
    fetcherService.authorizer = self.authorization;
    
    // Creates a fetcher for the API call.
    
    

    NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id,snippet,contentDetails,status"];
    GTMSessionFetcher *fetcher = [fetcherService fetcherWithURL:userinfoEndpoint];
    NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@{@"title":@"Test Video",@"scheduledStartTime":@"2024-01-30T00:00:00.000Z"},@"snippet",@{@"privacyStatus":@"public"},@"status", nil];
    fetcher.bodyData = [NSKeyedArchiver archivedDataWithRootObject:dict];
    
    [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
        // Checks for an error.
        if (error) {
            // OIDOAuthTokenErrorDomain indicates an issue with the authorization.
            if ([error.domain isEqual:OIDOAuthTokenErrorDomain]) {
                [self setGtmAuthorization:nil];
                [self logMessage:@"Authorization error during token refresh, clearing state. %@", error];
                // Other errors are assumed transient.
            } else {
                [self logMessage:@"Transient error during token refresh. %@", error];
            }
            return;
        }
        
        // Parses the JSON response.
        NSError *jsonError = nil;
        id jsonDictionaryOrArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
        
        // JSON error.
        if (jsonError) {
            [self logMessage:@"JSON decoding error %@", jsonError];
            return;
        }
        
        // Success response!
        [self logMessage:@"Success: %@", jsonDictionaryOrArray];
    }];

Error

Transient error during token refresh. Error Domain=com.google.HTTPStatus Code=400 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)" UserInfo=0x7fa156e14bb0 {data=<7b0a2022 6572726f 72223a20 7b0a2020 22657272 6f727322 3a205b0a 2020207b 0a202020 2022646f 6d61696e 223a2022 676c6f62 616c222c 0a202020 20227265 61736f6e 223a2022 70617273 65457272 6f72222c 0a202020 20226d65 73736167 65223a20 22546869 73204150 4920646f 6573206e 6f742073 7570706f 72742070 61727369 6e672066 6f726d2d 656e636f 64656420 696e7075 742e220a 2020207d 0a20205d 2c0a2020 22636f64 65223a20 3430302c 0a202022 6d657373 61676522 3a202254 68697320 41504920 646f6573 206e6f74 20737570 706f7274 20706172 73696e67 20666f72 6d2d656e 636f6465 6420696e 7075742e 220a207d 0a7d0a>}

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.