From 6ca4b993a9d8fbb41b5512097a7ebfbca3b5560a Mon Sep 17 00:00:00 2001 From: Bill Bunting Date: Sun, 2 Feb 2025 17:11:57 -0500 Subject: [PATCH] AWSGoogleSignIn upgrade to GoogleSignIn 8.0.0 --- .../GoogleHeaders/GIDAuthentication.h | 69 +--- .../GoogleHeaders/GIDConfiguration.h | 77 +++++ .../GoogleHeaders/GIDGoogleUser.h | 115 +++++-- .../Dependencies/GoogleHeaders/GIDSignIn.h | 314 +++++++++++------- .../GoogleHeaders/GIDSignInButton.h | 36 +- .../GoogleHeaders/GIDSignInResult.h | 41 +++ .../Dependencies/GoogleHeaders/GIDToken.h | 46 +++ .../Dependencies/GoogleHeaders/GoogleSignIn.h | 3 + .../AWSGoogleSignIn/AWSGoogleSignInProvider.m | 89 +++-- AWSGoogleSignIn.podspec | 1 + 10 files changed, 544 insertions(+), 247 deletions(-) create mode 100644 AWSAuthSDK/Dependencies/GoogleHeaders/GIDConfiguration.h create mode 100644 AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInResult.h create mode 100644 AWSAuthSDK/Dependencies/GoogleHeaders/GIDToken.h diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDAuthentication.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDAuthentication.h index 3571c6e8035..e38ee151780 100644 --- a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDAuthentication.h +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDAuthentication.h @@ -1,65 +1,32 @@ /* - * GIDAuthentication.h - * Google Sign-In iOS SDK + * Copyright 2022 Google LLC * - * Copyright 2014 Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Use of this SDK is subject to the Google APIs Terms of Service: - * https://developers.google.com/terms/ + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #import -@protocol GTMFetcherAuthorizationProtocol; -@class GIDAuthentication; - -/// The callback block that takes a `GIDAuthentication`, or an error if attempt -/// to refresh was unsuccessful. -typedef void (^GIDAuthenticationHandler)(GIDAuthentication *authentication, NSError *error); +@class OIDAuthState; -/// The callback block that takes an access token, or an error if attempt to refresh was -/// unsuccessful. -typedef void (^GIDAccessTokenHandler)(NSString *accessToken, NSError *error); +NS_ASSUME_NONNULL_BEGIN -/// This class represents the OAuth 2.0 entities needed for sign-in. +// Internal class for GIDGoogleUser NSCoding backward compatibility. @interface GIDAuthentication : NSObject -/// The client ID associated with the authentication. -@property(nonatomic, readonly) NSString *clientID; - -/// The OAuth2 access token to access Google services. -@property(nonatomic, readonly) NSString *accessToken; - -/// The estimated expiration date of the access token. -@property(nonatomic, readonly) NSDate *accessTokenExpirationDate; - -/// The OAuth2 refresh token to exchange for new access tokens. -@property(nonatomic, readonly) NSString *refreshToken; +@property(nonatomic) OIDAuthState* authState; -/// An OpenID Connect ID token that identifies the user. Send this token to your server to -/// authenticate the user there. For more information on this topic, see -/// https://developers.google.com/identity/sign-in/ios/backend-auth -@property(nonatomic, readonly) NSString *idToken; - -/// The estimated expiration date of the ID token. -@property(nonatomic, readonly) NSDate *idTokenExpirationDate; - -/// Gets a new authorizer for `GTLService`, `GTMSessionFetcher`, or `GTMHTTPFetcher`. -/// -/// @return A new authorizer -- (id)fetcherAuthorizer; - -/// Get a valid access token and a valid ID token, refreshing them first if they have expired or are -/// about to expire. -/// -/// @param handler A callback block that takes a `GIDAuthentication`, or an -/// error if attempt to refresh was unsuccessful. -- (void)getTokensWithHandler:(GIDAuthenticationHandler)handler; - -/// Refreshes the access token and the ID token using the refresh token. -/// -/// @param handler A callback block that takes a `GIDAuthentication`, or an -/// error if attempt to refresh was unsuccessful. -- (void)refreshTokensWithHandler:(GIDAuthenticationHandler)handler; +- (instancetype)initWithAuthState:(OIDAuthState *)authState; @end + +NS_ASSUME_NONNULL_END diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDConfiguration.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDConfiguration.h new file mode 100644 index 00000000000..303532f9e57 --- /dev/null +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDConfiguration.h @@ -0,0 +1,77 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// This class represents the client configuration provided by the developer. +@interface GIDConfiguration : NSObject + +/// The client ID of the app from the Google Cloud Console. +@property(nonatomic, readonly) NSString *clientID; + +/// The client ID of the home server. This will be returned as the `audience` property of the +/// OpenID Connect ID token. For more info on the ID token: +/// https://developers.google.com/identity/sign-in/ios/backend-auth +@property(nonatomic, readonly, nullable) NSString *serverClientID; + +/// The Google Apps domain to which users must belong to sign in. To verify, check +/// `GIDGoogleUser`'s `hostedDomain` property. +@property(nonatomic, readonly, nullable) NSString *hostedDomain; + +/// The OpenID2 realm of the home server. This allows Google to include the user's OpenID +/// Identifier in the OpenID Connect ID token. +@property(nonatomic, readonly, nullable) NSString *openIDRealm; + +/// Unavailable. Please use `initWithClientID:` or one of the other initializers below. +/// :nodoc: ++ (instancetype)new NS_UNAVAILABLE; + +/// Unavailable. Please use `initWithClientID:` or one of the other initializers below. +/// :nodoc: +- (instancetype)init NS_UNAVAILABLE; + +/// Initialize a `GIDConfiguration` object with a client ID. +/// +/// @param clientID The client ID of the app. +/// @return An initialized `GIDConfiguration` instance. +- (instancetype)initWithClientID:(NSString *)clientID; + +/// Initialize a `GIDConfiguration` object with a client ID and server client ID. +/// +/// @param clientID The client ID of the app. +/// @param serverClientID The server's client ID. +/// @return An initialized `GIDConfiguration` instance. +- (instancetype)initWithClientID:(NSString *)clientID + serverClientID:(nullable NSString *)serverClientID; + +/// Initialize a `GIDConfiguration` object by specifying all available properties. +/// +/// @param clientID The client ID of the app. +/// @param serverClientID The server's client ID. +/// @param hostedDomain The Google Apps domain to be used. +/// @param openIDRealm The OpenID realm to be used. +/// @return An initialized `GIDConfiguration` instance. +- (instancetype)initWithClientID:(NSString *)clientID + serverClientID:(nullable NSString *)serverClientID + hostedDomain:(nullable NSString *)hostedDomain + openIDRealm:(nullable NSString *)openIDRealm NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDGoogleUser.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDGoogleUser.h index 17e6e5ea654..f4d08b3021c 100644 --- a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDGoogleUser.h +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDGoogleUser.h @@ -1,39 +1,114 @@ /* - * GIDGoogleUser.h - * Google Sign-In iOS SDK + * Copyright 2022 Google LLC * - * Copyright 2014 Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Use of this SDK is subject to the Google APIs Terms of Service: - * https://developers.google.com/terms/ + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #import +#import + +#if __has_include() +#import +#elif __has_include() +#import +#endif -@class GIDAuthentication; + +@class GIDConfiguration; +@class GIDSignInResult; +@class GIDToken; @class GIDProfileData; -/// This class represents a user account. +NS_ASSUME_NONNULL_BEGIN + +/// This class represents a signed-in user. @interface GIDGoogleUser : NSObject /// The Google user ID. -@property(nonatomic, readonly) NSString *userID; +@property(nonatomic, readonly, nullable) NSString *userID; + +/// The basic profile data for the user. +@property(nonatomic, readonly, nullable) GIDProfileData *profile; + +/// The OAuth2 scopes granted to the app in an array of `NSString`. +@property(nonatomic, readonly, nullable) NSArray *grantedScopes; + +/// The configuration that was used to sign in this user. +@property(nonatomic, readonly) GIDConfiguration *configuration; -/// Representation of the Basic profile data. It is only available if -/// `GIDSignIn.shouldFetchBasicProfile` is set and either `-[GIDSignIn signIn]` or -/// `-[GIDSignIn restorePreviousSignIn]` has been completed successfully. -@property(nonatomic, readonly) GIDProfileData *profile; +/// The OAuth2 access token to access Google services. +@property(nonatomic, readonly) GIDToken *accessToken; -/// The authentication object for the user. -@property(nonatomic, readonly) GIDAuthentication *authentication; +/// The OAuth2 refresh token to exchange for new access tokens. +@property(nonatomic, readonly) GIDToken *refreshToken; -/// The API scopes granted to the app in an array of `NSString`. -@property(nonatomic, readonly) NSArray *grantedScopes; +/// The OpenID Connect ID token that identifies the user. +/// +/// Send this token to your server to authenticate the user there. For more information on this topic, +/// see https://developers.google.com/identity/sign-in/ios/backend-auth. +@property(nonatomic, readonly, nullable) GIDToken *idToken; -/// For Google Apps hosted accounts, the domain of the user. -@property(nonatomic, readonly) NSString *hostedDomain; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +/// The authorizer for use with `GTLRService`, `GTMSessionFetcher`, or `GTMHTTPFetcher`. +// @property(nonatomic, readonly) id fetcherAuthorizer; +#pragma clang diagnostic pop -/// An OAuth2 authorization code for the home server. -@property(nonatomic, readonly) NSString *serverAuthCode; +/// Refresh the user's access and ID tokens if they have expired or are about to expire. +/// +/// @param completion A completion block that takes a `GIDGoogleUser` or an error if the attempt to +/// refresh tokens was unsuccessful. The block will be called asynchronously on the main queue. +- (void)refreshTokensIfNeededWithCompletion:(void (^)(GIDGoogleUser *_Nullable user, + NSError *_Nullable error))completion; + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +/// Starts an interactive consent flow on iOS to add new scopes to the user's `grantedScopes`. +/// +/// The completion will be called at the end of this process. If successful, a `GIDSignInResult` +/// instance will be returned reflecting the new scopes and saved sign-in state will be updated. +/// +/// @param scopes The scopes to ask the user to consent to. +/// @param presentingViewController The view controller used to present `SFSafariViewController` on +/// iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on +/// iOS 13+. +/// @param completion The optional block that is called on completion. This block will be called +/// asynchronously on the main queue. +- (void)addScopes:(NSArray *)scopes + presentingViewController:(UIViewController *)presentingViewController + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion + NS_EXTENSION_UNAVAILABLE("The add scopes flow is not supported in App Extensions."); + +#elif TARGET_OS_OSX + +/// Starts an interactive consent flow on macOS to add new scopes to the user's `grantedScopes`. +/// +/// The completion will be called at the end of this process. If successful, a `GIDSignInResult` +/// instance will be returned reflecting the new scopes and saved sign-in state will be updated. +/// +/// @param scopes An array of scopes to ask the user to consent to. +/// @param presentingWindow The window used to supply `presentationContextProvider` for +/// `ASWebAuthenticationSession`. +/// @param completion The optional block that is called on completion. This block will be called +/// asynchronously on the main queue. +- (void)addScopes:(NSArray *)scopes + presentingWindow:(NSWindow *)presentingWindow + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion; + +#endif @end + +NS_ASSUME_NONNULL_END diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignIn.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignIn.h index 12046159ef7..2751097e106 100644 --- a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignIn.h +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignIn.h @@ -1,24 +1,39 @@ /* - * GIDSignIn.h - * Google Sign-In iOS SDK + * Copyright 2021 Google LLC * - * Copyright 2012 Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Use of this SDK is subject to the Google APIs Terms of Service: - * https://developers.google.com/terms/ + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #import +#import + +#if __has_include() #import +#elif __has_include() +#import +#endif +@class GIDConfiguration; @class GIDGoogleUser; -@class GIDSignIn; +@class GIDSignInResult; + +NS_ASSUME_NONNULL_BEGIN -/// The error domain for `NSError`s returned by the Google Identity SDK. -extern NSString *const kGIDSignInErrorDomain; +/// The error domain for `NSError`s returned by the Google Sign-In SDK. +extern NSErrorDomain const kGIDSignInErrorDomain; -/// A list of potential error codes returned from the Google Identity SDK. -typedef NS_ENUM(NSInteger, GIDSignInErrorCode) { +/// A list of potential error codes returned from the Google Sign-In SDK. +typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { /// Indicates an unknown error has occurred. kGIDSignInErrorCodeUnknown = -1, /// Indicates a problem reading or writing to the application keychain. @@ -30,141 +45,212 @@ typedef NS_ENUM(NSInteger, GIDSignInErrorCode) { kGIDSignInErrorCodeCanceled = -5, /// Indicates an Enterprise Mobility Management related error has occurred. kGIDSignInErrorCodeEMM = -6, + /// Indicates the requested scopes have already been granted to the `currentUser`. + kGIDSignInErrorCodeScopesAlreadyGranted = -8, + /// Indicates there is an operation on a previous user. + kGIDSignInErrorCodeMismatchWithCurrentUser = -9, }; -/// A protocol implemented by the delegate of `GIDSignIn` to receive a refresh token or an error. -@protocol GIDSignInDelegate - -/// The sign-in flow has finished and was successful if `error` is `nil`. -- (void)signIn:(GIDSignIn *)signIn - didSignInForUser:(GIDGoogleUser *)user - withError:(NSError *)error; - -@optional - -/// Finished disconnecting `user` from the app successfully if `error` is `nil`. -- (void)signIn:(GIDSignIn *)signIn - didDisconnectWithUser:(GIDGoogleUser *)user - withError:(NSError *)error; - -@end - -/// This class signs the user in with Google. It also provides single sign-on via a capable Google -/// app if one is installed. +/// This class is used to sign in users with their Google account and manage their session. /// -/// For reference, please see "Google Sign-In for iOS" at +/// For reference, please see "Google Sign-In for iOS and macOS" at /// https://developers.google.com/identity/sign-in/ios -/// -/// Here is sample code to use `GIDSignIn`: -/// 1. Get a reference to the `GIDSignIn` shared instance: -/// ``` -/// GIDSignIn *signIn = [GIDSignIn sharedInstance]; -/// ``` -/// 2. Call `[signIn setDelegate:self]`; -/// 3. Set up delegate method `signIn:didSignInForUser:withError:`. -/// 4. Call `handleURL` on the shared instance from `application:openUrl:...` in your app delegate. -/// 5. Call `signIn` on the shared instance; @interface GIDSignIn : NSObject -/// The authentication object for the current user, or `nil` if there is currently no logged in -/// user. -@property(nonatomic, readonly) GIDGoogleUser *currentUser; +/// The shared `GIDSignIn` instance. +@property(class, nonatomic, readonly) GIDSignIn *sharedInstance; -/// The object to be notified when authentication is finished. -@property(nonatomic, weak) id delegate; +/// The `GIDGoogleUser` object representing the current user or `nil` if there is no signed-in user. +@property(nonatomic, readonly, nullable) GIDGoogleUser *currentUser; -/// The view controller used to present `SFSafariViewContoller` on iOS 9 and 10. -@property(nonatomic, weak) UIViewController *presentingViewController; +/// The active configuration for this instance of `GIDSignIn`. +@property(nonatomic, nullable) GIDConfiguration *configuration; -/// The client ID of the app from the Google APIs console. Must set for sign-in to work. -@property(nonatomic, copy) NSString *clientID; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -/// The API scopes requested by the app in an array of `NSString`s. The default value is `@[]`. +/// Configures `GIDSignIn` for use. /// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSArray *scopes; - -/// Whether or not to fetch basic profile data after signing in. The data is saved in the -/// `GIDGoogleUser.profileData` object. +/// @param completion A nullable callback block passing back any error arising from the +/// configuration process if any exists. /// -/// Setting the flag will add "email" and "profile" to scopes. -/// Defaults to `YES`. -@property(nonatomic, assign) BOOL shouldFetchBasicProfile; +/// Call this method on `GIDSignIn` prior to use and as early as possible. This method generates App +/// Attest key IDs and the attestation object eagerly to minimize latency later on during the sign +/// in or add scopes flows. +- (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable error))completion +NS_SWIFT_NAME(configure(completion:)); -/// The language for sign-in, in the form of ISO 639-1 language code optionally followed by a dash -/// and ISO 3166-1 alpha-2 region code, such as `@"it"` or `@"pt-PT"`. Only set if different from -/// system default. +/// Configures `GIDSignIn` for use in debug or test environments. /// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSString *language; - -/// The login hint to the authorization server, for example the user's ID, or email address, -/// to be prefilled if possible. +/// @param APIKey The API Key to use during configuration of the App Check debug provider. +/// @param completion A nullable callback block passing back any error arising from the +/// configuration process if any exists. /// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSString *loginHint; - -/// The client ID of the home web server. This will be returned as the `audience` property of the -/// OpenID Connect ID token. For more info on the ID token: -/// https://developers.google.com/identity/sign-in/ios/backend-auth -/// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSString *serverClientID; - -/// The OpenID2 realm of the home web server. This allows Google to include the user's OpenID -/// Identifier in the OpenID Connect ID token. -/// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSString *openIDRealm; - -/// The Google Apps domain to which users must belong to sign in. To verify, check -/// `GIDGoogleUser`'s `hostedDomain` property. -/// -/// This property is optional. If you set it, set it before calling `signIn`. -@property(nonatomic, copy) NSString *hostedDomain; - -/// Returns a shared `GIDSignIn` instance. -+ (GIDSignIn *)sharedInstance; - -/// Unavailable. Use `sharedInstance` to instantiate `GIDSignIn`. +/// Call this method on `GIDSignIn` prior to use and as early as possible. This method generates App +/// Attest key IDs and the attestation object eagerly to minimize latency later on during the sign +/// in or add scopes flows. +- (void)configureDebugProviderWithAPIKey:(NSString *)APIKey + completion:(nullable void (^)(NSError * _Nullable error))completion +API_AVAILABLE(ios(14)) +NS_SWIFT_NAME(configureDebugProvider(withAPIKey:completion:)); + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +/// Unavailable. Use the `sharedInstance` property to instantiate `GIDSignIn`. +/// :nodoc: + (instancetype)new NS_UNAVAILABLE; -/// Unavailable. Use `sharedInstance` to instantiate `GIDSignIn`. +/// Unavailable. Use the `sharedInstance` property to instantiate `GIDSignIn`. +/// :nodoc: - (instancetype)init NS_UNAVAILABLE; -/// This method should be called from your `UIApplicationDelegate`'s `application:openURL:options` -/// and `application:openURL:sourceApplication:annotation` method(s). +/// This method should be called from your `UIApplicationDelegate`'s `application:openURL:options:` +/// method. /// /// @param url The URL that was passed to the app. /// @return `YES` if `GIDSignIn` handled this URL. - (BOOL)handleURL:(NSURL *)url; -/// Checks if there is a previously authenticated user saved in keychain. +/// Checks if there is a previous user sign-in saved in keychain. /// -/// @return `YES` if there is a previously authenticated user saved in keychain. +/// @return `YES` if there is a previous user sign-in saved in keychain. - (BOOL)hasPreviousSignIn; -/// Attempts to restore a previously authenticated user without interaction. +/// Attempts to restore a previous user sign-in without interaction. +/// +/// Restores user from the local cache and refreshes tokens if they have expired (>1 hour). +/// +/// @param completion The block that is called on completion. This block will be called asynchronously +/// on the main queue. +- (void)restorePreviousSignInWithCompletion:(nullable void (^)(GIDGoogleUser *_Nullable user, + NSError *_Nullable error))completion; -/// The delegate will be -/// called at the end of this process indicating success or failure. The current values of -/// `GIDSignIn`'s configuration properties will not impact the restored user. -- (void)restorePreviousSignIn; +/// Signs out the `currentUser`, removing it from the keychain. +- (void)signOut; -/// Starts an interactive sign-in flow using `GIDSignIn`'s configuration properties. +/// Disconnects the `currentUser` by signing them out and revoking all OAuth2 scope grants made to the app. /// -/// The delegate -/// will be called at the end of this process. Any saved sign-in state will be replaced by the -/// result of this flow. Note that this method should not be called when the app is starting up, -/// (e.g in `application:didFinishLaunchingWithOptions:`); instead use the `restorePreviousSignIn` -/// method to restore a previous sign-in. -- (void)signIn; +/// @param completion The optional block that is called on completion. +/// This block will be called asynchronously on the main queue. +- (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion; -/// Marks current user as being in the signed out state. -- (void)signOut; +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST -/// Disconnects the current user from the app and revokes previous authentication. If the operation -/// succeeds, the OAuth 2.0 token is also removed from keychain. -- (void)disconnect; +/// Starts an interactive sign-in flow on iOS. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingViewController The view controller used to present `SFSafariViewController` on +/// iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on +/// iOS 13+. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + completion: + (nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion + NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); + +/// Starts an interactive sign-in flow on iOS using the provided hint. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingViewController The view controller used to present `SFSafariViewController` on +/// iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on +/// iOS 13+. +/// @param hint An optional hint for the authorization server, for example the user's ID or email +/// address, to be prefilled if possible. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + hint:(nullable NSString *)hint + completion: +(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion +NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); + +/// Starts an interactive sign-in flow on iOS using the provided hint and additional scopes. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingViewController The view controller used to present `SFSafariViewController` on +/// iOS 9 and 10. +/// @param hint An optional hint for the authorization server, for example the user's ID or email +/// address, to be prefilled if possible. +/// @param additionalScopes An optional array of scopes to request in addition to the basic profile scopes. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + completion: +(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion +NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); + +#elif TARGET_OS_OSX + +/// Starts an interactive sign-in flow on macOS. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion; + +/// Starts an interactive sign-in flow on macOS using the provided hint. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`. +/// @param hint An optional hint for the authorization server, for example the user's ID or email +/// address, to be prefilled if possible. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion; + +/// Starts an interactive sign-in flow on macOS using the provided hint. +/// +/// The completion will be called at the end of this process. Any saved sign-in state will be +/// replaced by the result of this flow. Note that this method should not be called when the app is +/// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the +/// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in. +/// +/// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`. +/// @param hint An optional hint for the authorization server, for example the user's ID or email +/// address, to be prefilled if possible. +/// @param additionalScopes An optional array of scopes to request in addition to the basic profile scopes. +/// @param completion The optional block that is called on completion. This block will +/// be called asynchronously on the main queue. +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion; + +#endif @end + +NS_ASSUME_NONNULL_END + diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInButton.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInButton.h index 795f56988f8..f4cff9bd642 100644 --- a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInButton.h +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInButton.h @@ -1,15 +1,26 @@ /* - * GIDSignInButton.h - * Google Sign-In iOS SDK + * Copyright 2021 Google LLC * - * Copyright 2012 Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Use of this SDK is subject to the Google APIs Terms of Service: - * https://developers.google.com/terms/ + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST #import +NS_ASSUME_NONNULL_BEGIN + /// The layout styles supported by the `GIDSignInButton`. /// /// The minimum size of the button depends on the language used for text. @@ -31,11 +42,11 @@ typedef NS_ENUM(NSInteger, GIDSignInButtonColorScheme) { /// This class provides the "Sign in with Google" button. /// -/// You can instantiate this class programmatically or from a NIB file. You -/// should set up the `GIDSignIn` shared instance with your client ID and any -/// additional scopes, implement the delegate methods for `GIDSignIn`, and add -/// this button to your view hierarchy. -@interface GIDSignInButton : UIControl +/// You can instantiate this class programmatically or from a NIB file. You should connect this +/// control to an `IBAction`, or something similar, that calls +/// signInWithPresentingViewController:completion: on `GIDSignIn` and add it to your view +/// hierarchy. +@interface GIDSignInButton : UIControl /// The layout style for the sign-in button. /// Possible values: @@ -51,3 +62,8 @@ typedef NS_ENUM(NSInteger, GIDSignInButtonColorScheme) { @property(nonatomic, assign) GIDSignInButtonColorScheme colorScheme; @end + +NS_ASSUME_NONNULL_END + +#endif + diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInResult.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInResult.h new file mode 100644 index 00000000000..426a24c13c6 --- /dev/null +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDSignInResult.h @@ -0,0 +1,41 @@ +/* +* Copyright 2022 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#import + +@class GIDGoogleUser; + +NS_ASSUME_NONNULL_BEGIN + +/// A helper object that contains the result of a successful signIn or addScopes flow. +@interface GIDSignInResult : NSObject + +/// The updated `GIDGoogleUser` instance for the user who just completed the flow. +@property(nonatomic, readonly) GIDGoogleUser *user; + +/// An OAuth2 authorization code for the home server. +@property(nonatomic, readonly, nullable) NSString *serverAuthCode; + +/// Unsupported. ++ (instancetype)new NS_UNAVAILABLE; + +/// Unsupported. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GIDToken.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDToken.h new file mode 100644 index 00000000000..d013a767f1b --- /dev/null +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GIDToken.h @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// This class represents an OAuth2 or OpenID Connect token. +@interface GIDToken : NSObject + +/// The token string. +@property(nonatomic, copy, readonly) NSString *tokenString; + +/// The estimated expiration date of the token. +@property(nonatomic, readonly, nullable) NSDate *expirationDate; + +/// Check if current token is equal to another one. +/// +/// @param otherToken Another token to compare. +- (BOOL)isEqualToToken:(GIDToken *)otherToken; + +/// Unavailable. +/// :nodoc: ++ (instancetype)new NS_UNAVAILABLE; + +/// Unavailable. +/// :nodoc: +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/AWSAuthSDK/Dependencies/GoogleHeaders/GoogleSignIn.h b/AWSAuthSDK/Dependencies/GoogleHeaders/GoogleSignIn.h index fba52819f3c..72598cd23a4 100644 --- a/AWSAuthSDK/Dependencies/GoogleHeaders/GoogleSignIn.h +++ b/AWSAuthSDK/Dependencies/GoogleHeaders/GoogleSignIn.h @@ -12,5 +12,8 @@ #import "GIDProfileData.h" #import "GIDSignIn.h" #import "GIDSignInButton.h" +#import "GIDConfiguration.h" +#import "GIDToken.h" +#import "GIDSignInResult.h" #endif diff --git a/AWSAuthSDK/Sources/AWSGoogleSignIn/AWSGoogleSignInProvider.m b/AWSAuthSDK/Sources/AWSGoogleSignIn/AWSGoogleSignInProvider.m index 326ad6725e5..276e77804b6 100755 --- a/AWSAuthSDK/Sources/AWSGoogleSignIn/AWSGoogleSignInProvider.m +++ b/AWSAuthSDK/Sources/AWSGoogleSignIn/AWSGoogleSignInProvider.m @@ -18,8 +18,6 @@ #import "GoogleSignIn.h" -static NSString *const AWSGoogleSignInProviderClientScope = @"profile"; -static NSString *const AWSGoogleSignInProviderOIDCScope = @"openid"; static NSTimeInterval const AWSGoogleSignInProviderTokenRefreshBuffer = 10 * 60; static int64_t const AWSGoogleSignInProviderTokenRefreshTimeout = 60 * NSEC_PER_SEC; @@ -31,7 +29,7 @@ - (void)completeLogin; @end -@interface AWSGoogleSignInProvider() +@interface AWSGoogleSignInProvider() @property (atomic, strong) AWSTaskCompletionSource *taskCompletionSource; @property (nonatomic, strong) dispatch_semaphore_t semaphore; @@ -61,7 +59,6 @@ + (instancetype)sharedInstance { AWSServiceInfo *serviceInfo = [[AWSInfo defaultAWSInfo] defaultServiceInfo:AWSInfoIdentityManager]; googleClientID = [[serviceInfo.infoDictionary objectForKey:AWSInfoGoogleIdentifierLegacy] objectForKey:AWSInfoGoogleClientIdLegacy]; } - if (!googleClientID) { @throw [NSException exceptionWithName:NSInternalInconsistencyException @@ -73,15 +70,13 @@ + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [[AWSGoogleSignInProvider alloc] initWithGoogleClientID:googleClientID]; - if ([_sharedInstance isConfigurationKeyPresent]) { - [_sharedInstance setScopes:[_sharedInstance getPermissionsFromConfig]]; - } }); return _sharedInstance; } - (instancetype)initWithGoogleClientID:(NSString *)googleClientID { + if (self = [super init]) { _semaphore = dispatch_semaphore_create(0); @@ -89,11 +84,14 @@ - (instancetype)initWithGoogleClientID:(NSString *)googleClientID { _executor = [AWSExecutor executorWithOperationQueue:operationQueue]; _signInViewController = nil; - _signInClient = [NSClassFromString(@"GIDSignIn") sharedInstance]; - self.signInClient.delegate = self; - self.signInClient.clientID = googleClientID; - self.signInClient.scopes = @[AWSGoogleSignInProviderClientScope, AWSGoogleSignInProviderOIDCScope]; + + Class GIDConfigurationClass = NSClassFromString(@"GIDConfiguration"); + SEL initializerSelector = @selector(initWithClientID:serverClientID:); + GIDConfiguration *config = [[GIDConfigurationClass alloc] performSelector:initializerSelector withObject:googleClientID]; + self.signInClient.configuration = config; + + [self.signInClient configureWithCompletion:nil]; } return self; @@ -101,10 +99,6 @@ - (instancetype)initWithGoogleClientID:(NSString *)googleClientID { #pragma mark - Hub user interface -- (void)setScopes:(NSArray *)scopes { - self.signInClient.scopes = scopes; -} - - (void)setViewControllerForGoogleSignIn:(UIViewController *)signInViewController { self.signInViewController = signInViewController; } @@ -130,8 +124,8 @@ - (NSString *)identityProviderName { AWSTask *task = [AWSTask taskWithResult:nil]; return [task continueWithExecutor:self.executor withBlock:^id _Nullable(AWSTask * _Nonnull task) { - NSString *idToken = self.signInClient.currentUser.authentication.idToken; - NSDate *idTokenExpirationDate = self.signInClient.currentUser.authentication.idTokenExpirationDate; + NSString *idToken = self.signInClient.currentUser.idToken.tokenString; + NSDate *idTokenExpirationDate = self.signInClient.currentUser.idToken.expirationDate; if (idToken // If the cached token expires within 10 min, tries refreshing a token. @@ -149,8 +143,8 @@ - (NSString *)identityProviderName { } } - idToken = self.signInClient.currentUser.authentication.idToken; - idTokenExpirationDate = self.signInClient.currentUser.authentication.idTokenExpirationDate; + idToken = self.signInClient.currentUser.idToken.tokenString; + idTokenExpirationDate = self.signInClient.currentUser.idToken.expirationDate; if (idToken // If the cached token expires within 10 min, tries refreshing a token. @@ -158,7 +152,7 @@ - (NSString *)identityProviderName { return [AWSTask taskWithResult:idToken]; } - // `self.taskCompletionSource` is used to convert the `GIDSignInDelegate` method to a block based method. + // Legacy: `self.taskCompletionSource` is used to convert the `GIDSignInDelegate` method to a block based method. // The `token` string or an error object is returned in a block when the delegate method is called later. // See the `GIDSignInDelegate` section of this file. self.taskCompletionSource = [AWSTaskCompletionSource taskCompletionSource]; @@ -170,7 +164,19 @@ - (NSString *)identityProviderName { } - (void)signInSilently { - [self.signInClient restorePreviousSignIn]; + + [self.signInClient restorePreviousSignInWithCompletion:^(GIDGoogleUser * _Nullable user, + NSError * _Nullable error) { + if (error) { + // Show the app's signed-out state. + NSLog(@"Error during silent sign-on."); + } else { + NSLog(@"Success during silent sign-on."); + [self didSignInForUser:user withError:error]; + } + + }]; + } #pragma mark - AWSSignInProvider @@ -187,17 +193,20 @@ - (void)reloadSession { - (void)login:(AWSSignInManagerCompletionBlock)completionHandler { self.completionHandler = completionHandler; - self.signInClient.presentingViewController = self.signInViewController; - [self.signInClient signIn]; + + [self.signInClient + signInWithPresentingViewController:self.signInViewController + completion:^(GIDSignInResult * _Nullable signInResult, + NSError * _Nullable error) { + [self didSignInForUser:signInResult.user withError:error]; + }]; } - (void)logout { - [self.signInClient disconnect]; + [self.signInClient signOut]; } -#pragma mark - GIDSignInDelegate - -- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error { +- (void) didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error { // `self.taskCompletionSource` is used to return `user.authentication.idToken` or `error` to the `- token` method. // See the `AWSIdentityProvider` section of this file. if (error) { @@ -211,7 +220,7 @@ - (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withEr } } else { if (self.taskCompletionSource) { - self.taskCompletionSource.result = user.authentication.idToken; + self.taskCompletionSource.result = user.idToken; self.taskCompletionSource = nil; } [self completeLoginWithToken]; @@ -229,30 +238,6 @@ - (void)completeLoginWithToken { [[AWSSignInManager sharedInstance] completeLogin]; } -#pragma mark - GIDSignInUIDelegate (Removed) - -/// These methods should never be called since GIDSignInUIDelegate no longer exists. -/// However, we'll retain them in case there is some code path we're not aware of that -/// invokes it. - -- (void)signInWillDispatch:(GIDSignIn *)signIn error:(NSError *)error { - AWSDDLogError(@"Error: Invoked removed GIDSignInUIDelegate method: %s", __func__); - if (error) { - AWSDDLogError(@"Error: %@", error); - } -} - -- (void)signIn:(GIDSignIn *)signIn -presentViewController:(UIViewController *)viewController { - AWSDDLogError(@"Error: Invoked removed GIDSignInUIDelegate method: %s", __func__); -} - -- (void)signIn:(GIDSignIn *)signIn -dismissViewController:(UIViewController *)viewController { - AWSDDLogError(@"Error: Invoked removed GIDSignInUIDelegate method: %s", __func__); - [viewController dismissViewControllerAnimated:YES completion:nil]; -} - #pragma mark - Application delegates - (BOOL)interceptApplication:(UIApplication *)application diff --git a/AWSGoogleSignIn.podspec b/AWSGoogleSignIn.podspec index a63fcf59a2a..e68ab237294 100644 --- a/AWSGoogleSignIn.podspec +++ b/AWSGoogleSignIn.podspec @@ -14,6 +14,7 @@ Pod::Spec.new do |s| s.requires_arc = true s.dependency 'AWSAuthCore', '2.40.1' s.dependency 'AWSCore', '2.40.1' + s.dependency 'GoogleSignIn', '8.0.0' s.source_files = 'AWSAuthSDK/Sources/AWSGoogleSignIn/*.{h,m}', 'AWSAuthSDK/Dependencies/GoogleHeaders/*.h' s.public_header_files = 'AWSAuthSDK/Sources/AWSGoogleSignIn/*.h' s.private_header_files = 'AWSAuthSDK/Dependencies/GoogleHeaders/*.h'