Skip to content

Commit b6ae13f

Browse files
authored
Remove keychain entries if fresh install (#567)
1 parent 31bdbf8 commit b6ae13f

File tree

4 files changed

+41
-6
lines changed

4 files changed

+41
-6
lines changed

GoogleSignIn/Sources/GIDSignIn.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@
120120
// Error string for user cancelations.
121121
static NSString *const kUserCanceledError = @"The user canceled the sign-in flow.";
122122

123-
// User preference key to detect fresh install of the app.
124-
static NSString *const kAppHasRunBeforeKey = @"GID_AppHasRunBefore";
123+
NSString *const kAppHasRunBeforeKey = @"GID_AppHasRunBefore";
125124

126125
// Maximum retry interval in seconds for the fetcher.
127126
static const NSTimeInterval kFetcherMaxRetryInterval = 15.0;
@@ -672,6 +671,11 @@ - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore
672671

673672
// Check to see if the 3P app is being run for the first time after a fresh install.
674673
BOOL isFreshInstall = [self isFreshInstall];
674+
675+
// If this is a fresh install, ensure that any pre-existing keychain data is purged.
676+
if (isFreshInstall) {
677+
[self removeAllKeychainEntries];
678+
}
675679

676680
NSString *authorizationEnpointURL = [NSString stringWithFormat:kAuthorizationURLTemplate,
677681
[GIDSignInPreferences googleAuthorizationServer]];

GoogleSignIn/Sources/GIDSignIn_Private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ NS_ASSUME_NONNULL_BEGIN
3232
@class GIDAppCheck;
3333
@class GIDAuthStateMigration;
3434

35+
/// User preference key to detect fresh install of the app.
36+
extern NSString *const kAppHasRunBeforeKey;
37+
3538
/// Represents a completion block that takes a `GIDSignInResult` on success or an error if the
3639
/// operation was unsuccessful.
3740
typedef void (^GIDSignInCompletion)(GIDSignInResult *_Nullable signInResult,

GoogleSignIn/Tests/Unit/GIDSignInTest.m

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@
120120
@"com.google.UnitTests:///emmcallback?action=unrecognized";
121121
static NSString * const kDevicePolicyAppBundleID = @"com.google.DevicePolicy";
122122

123-
static NSString * const kAppHasRunBeforeKey = @"GPP_AppHasRunBefore";
124-
125123
static NSString * const kFingerprintKeychainName = @"fingerprint";
126124
static NSString * const kVerifierKeychainName = @"verifier";
127125
static NSString * const kVerifierKey = @"verifier";
@@ -1212,6 +1210,19 @@ - (void)testNotHandleWrongPath {
12121210
XCTAssertFalse(_completionCalled, @"should not call delegate");
12131211
}
12141212

1213+
#pragma mark - Test Fresh Install
1214+
1215+
- (void)testFreshInstall_removesKeychainEntries {
1216+
// Simulate that the app has been deleted and user defaults removed.
1217+
[NSUserDefaults.standardUserDefaults removeObjectForKey:kAppHasRunBeforeKey];
1218+
// Initialization should check `isFreshInstall`.
1219+
GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore
1220+
authStateMigrationService:_authStateMigrationService];
1221+
// If `isFreshInstall`, keychain entries should be removed.
1222+
XCTAssertNotNil(signIn);
1223+
XCTAssertTrue(self->_keychainRemoved);
1224+
}
1225+
12151226
#pragma mark - Tests - disconnectWithCallback:
12161227

12171228
// Verifies disconnect calls callback with no errors if access token is present.

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ Google Sign-In allows your users to sign-in to your native macOS app using their
4848
and default browser. When building for macOS, the `signInWithConfiguration:` and `addScopes:`
4949
methods take a `presentingWindow:` parameter in place of `presentingViewController:`. Note that
5050
in order for your macOS app to store credentials via the Keychain on macOS, you will need to add
51-
`$(AppIdentifierPrefix)$(CFBundleIdentifier)` to its keychain access group.
51+
`$(AppIdentifierPrefix)$(CFBundleIdentifier)` as the first item in its keychain access group.
5252

5353
### Mac Catalyst
5454

5555
Google Sign-In also supports iOS apps that are built for macOS via
5656
[Mac Catalyst](https://developer.apple.com/mac-catalyst/). In order for your Mac Catalyst app
5757
to store credentials via the Keychain on macOS, you will need to add
58-
`$(AppIdentifierPrefix)$(CFBundleIdentifier)` to its keychain access group.
58+
`$(AppIdentifierPrefix)$(CFBundleIdentifier)` as the first item in the keychain
59+
access group.
5960

6061
## Using the Google Sign-In Button
6162

@@ -107,3 +108,19 @@ let signInButton = GoogleSignInButton {
107108
}
108109
let hostedButton = NSHostingView(rootView: signInButton)
109110
```
111+
112+
## A Note on iOS Keychain Access Groups
113+
114+
GSI uses your default (first listed) keychain access group. If you don't add a
115+
custom keychain access group, the default keychain access group is provided by
116+
Xcode and looks like `$(AppIdentifierPrefix)$(CFBundleIdentifier)`.
117+
118+
GSI [removes keychain items upon fresh install](https://github.com/google/GoogleSignIn-iOS/pull/567)
119+
to ensure that stale credentials from previous installs of your app are not
120+
mistakenly used. If your app uses a shared access group by default this may
121+
lead to new installs of apps sharing the same keychain access group to remove
122+
keychain credentials for apps already installed.
123+
124+
To prevent unintentional credential removal, you can explicitly list the
125+
typical default access group (or whatever you prefer so long as it is not
126+
shared) in your list first. GSI, will then use that default access group.

0 commit comments

Comments
 (0)