Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2a97c96
g-orchestrated: Add sanitized SDK wrapper identifier to GIDSignInPref…
w-goog Aug 5, 2026
cbdf28f
g-orchestrated: Emit gidwrapper logging parameter and expose wrapperI…
w-goog Aug 5, 2026
19c3415
g-orchestrated: Emit gidwrapper on GIDGoogleUser token refresh
w-goog Aug 5, 2026
c13440d
g-orchestrated: Changelog: add wrapperIdentifier / gidwrapper parameter
w-goog Aug 5, 2026
8a4ca11
g-orchestrated: Test wrapper identifier sanitizer and accessors
w-goog Aug 5, 2026
6765b97
g-orchestrated: Test gidwrapper emission on GIDSignIn requests
w-goog Aug 5, 2026
ed60d79
g-orchestrated: Test gidwrapper emission on token refresh
w-goog Aug 5, 2026
86ea6e9
g-orchestrated: Validate wrapper identifier instead of rewriting it
w-goog Aug 5, 2026
3c14cba
g-orchestrated: Build revoke URL structurally and consolidate logging…
w-goog Aug 5, 2026
eb938c5
g-orchestrated: Route token refresh through shared logging parameters
w-goog Aug 5, 2026
f5c1c10
g-orchestrated: Changelog: revise wrapperIdentifier entry
w-goog Aug 5, 2026
f68eb86
g-orchestrated: Test wrapper identifier validation and write-once
w-goog Aug 5, 2026
75f29a7
g-orchestrated: Test gidwrapper on revoke URL via query items
w-goog Aug 5, 2026
186d12e
g-orchestrated: Test logging parameters on refresh after consolidation
w-goog Aug 5, 2026
467893c
g-orchestrated: Match Android's wrapper identifier sanitization rules
w-goog Aug 8, 2026
f951088
g-orchestrated: Test the Android-matching wrapper identifier rules
w-goog Aug 8, 2026
b15b891
g-orchestrated: Format GIDSignInPreferences docs in the house style
w-goog Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Unreleased
- Add `GIDSignIn.wrapperIdentifier`, an optional property for SDKs embedding Google Sign-In to self-identify in Google's diagnostic logs via a new `gidwrapper` parameter. It accepts up to 100 printable ASCII characters; longer values are truncated, and values containing non-ASCII or control characters are dropped. It is opt-in and default behavior is unchanged.

# 9.2.0
- Expose the refresh token expiration date ([#577](https://github.com/google/GoogleSignIn-iOS/pull/577))
- Support requesting the `amr` (Authentication Methods References) claim ([#600](https://github.com/google/GoogleSignIn-iOS/pull/600))
Expand Down
3 changes: 1 addition & 2 deletions GoogleSignIn/Sources/GIDGoogleUser.m
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ - (void)refreshTokensIfNeededWithCompletion:(GIDGoogleUserCompletion)completion
[additionalParameters addEntriesFromDictionary:
self.authState.lastTokenResponse.request.additionalParameters];
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
additionalParameters[kSDKVersionLoggingParameter] = GIDVersion();
additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment();
[GIDSignInPreferences addLoggingParameters:additionalParameters];

OIDTokenRequest *tokenRefreshRequest =
[self.authState tokenRefreshRequestWithAdditionalParameters:additionalParameters];
Expand Down
52 changes: 39 additions & 13 deletions GoogleSignIn/Sources/GIDSignIn.m
Original file line number Diff line number Diff line change
Expand Up @@ -573,16 +573,36 @@ - (void)disconnectWithCompletion:(nullable GIDDisconnectCompletion)completion {
}
return;
}
NSString *revokeURLString = [NSString stringWithFormat:kRevokeTokenURLTemplate,
NSString *baseURLString = [NSString stringWithFormat:kRevokeTokenURLTemplate,
[GIDSignInPreferences googleAuthorizationServer], token];
// Append logging parameter
revokeURLString = [NSString stringWithFormat:@"%@&%@=%@&%@=%@",
revokeURLString,
kSDKVersionLoggingParameter,
GIDVersion(),
kEnvironmentLoggingParameter,
GIDEnvironment()];
NSURL *revokeURL = [NSURL URLWithString:revokeURLString];
NSURLComponents *components = [NSURLComponents componentsWithString:baseURLString];
NSURL *revokeURL;
if (components) {
NSMutableArray<NSURLQueryItem *> *items =
[components.queryItems mutableCopy] ?: [NSMutableArray array];

NSMutableDictionary<NSString *, NSString *> *loggingParams = [[NSMutableDictionary alloc] init];
[GIDSignInPreferences addLoggingParameters:loggingParams];
for (NSString *name in [loggingParams.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
[items addObject:[NSURLQueryItem queryItemWithName:name value:loggingParams[name]]];
}

components.queryItems = items;
revokeURL = components.URL;
}

if (!revokeURL) {
// The revoke URL could not be constructed, so the token was left untouched.
NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain
code:kGIDSignInErrorCodeUnknown
userInfo:nil];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error);
});
}
return;
}
[self startFetchURL:revokeURL
fromAuthState:authState
withComment:@"GIDSignIn: revoke tokens"
Expand Down Expand Up @@ -625,6 +645,14 @@ + (GIDSignIn *)sharedInstance {
return sharedInstance;
}

- (nullable NSString *)wrapperIdentifier {
return [GIDSignInPreferences wrapperIdentifier];
}

- (void)setWrapperIdentifier:(nullable NSString *)wrapperIdentifier {
[GIDSignInPreferences setWrapperIdentifier:wrapperIdentifier];
}

#pragma mark - Configuring and pre-warming

#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
Expand Down Expand Up @@ -918,8 +946,7 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
[additionalParameters addEntriesFromDictionary:options.extraParams];
#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST
additionalParameters[kSDKVersionLoggingParameter] = GIDVersion();
additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment();
[GIDSignInPreferences addLoggingParameters:additionalParameters];

return additionalParameters;
}
Expand Down Expand Up @@ -1054,8 +1081,7 @@ - (void)maybeFetchToken:(GIDAuthFlow *)authFlow {
emmSupport:authFlow.emmSupport
isPasscodeInfoRequired:passcodeInfoRequired.length > 0]];
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
additionalParameters[kSDKVersionLoggingParameter] = GIDVersion();
additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment();
[GIDSignInPreferences addLoggingParameters:additionalParameters];

OIDTokenRequest *tokenRequest;
if (!authState.lastTokenResponse.accessToken &&
Expand Down
32 changes: 28 additions & 4 deletions GoogleSignIn/Sources/GIDSignInPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,37 @@ NS_ASSUME_NONNULL_BEGIN

extern NSString *const kSDKVersionLoggingParameter;
extern NSString *const kEnvironmentLoggingParameter;

NSString* GIDVersion(void);

NSString* GIDEnvironment(void);
extern NSString *const kSDKWrapperLoggingParameter;

@interface GIDSignInPreferences : NSObject

/// Returns the current Google Sign-In SDK version.
+ (NSString *)sdkVersion;

/// Returns the current Apple execution environment, such as `ios` or `macos`.
+ (NSString *)environment;

/// Returns the current SDK wrapper identifier, or `nil` if none is set.
+ (nullable NSString *)wrapperIdentifier;

/// Sets the SDK wrapper identifier.
///
/// A value may be up to 100 printable ASCII characters; a longer value is truncated to its first
/// 100 characters. A value that is empty, or that contains any non-ASCII or ASCII control
/// character, is dropped entirely and asserts in debug builds.
///
/// The first accepted write wins; later differing writes are ignored. This method is thread-safe.
///
/// @param wrapperIdentifier The identifier to report, or `nil` to reset it.
+ (void)setWrapperIdentifier:(nullable NSString *)wrapperIdentifier;

/// Adds the standard logging parameters to the supplied dictionary.
///
/// The parameters are `gpsdk`, `gidenv`, and, when a wrapper identifier is set, `gidwrapper`.
///
/// @param params The dictionary to add the logging parameters to.
+ (void)addLoggingParameters:(NSMutableDictionary<NSString *, NSString *> *)params;

+ (NSString *)googleAuthorizationServer;
+ (NSString *)googleTokenServer;
+ (NSString *)googleUserInfoServer;
Expand Down
96 changes: 90 additions & 6 deletions GoogleSignIn/Sources/GIDSignInPreferences.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#import "GoogleSignIn/Sources/GIDSignInPreferences.h"

#import <os/lock.h>

NS_ASSUME_NONNULL_BEGIN

static NSString *const kLSOServer = @"accounts.google.com";
Expand All @@ -26,6 +28,9 @@
// The name of the query parameter used for logging the Apple execution environment.
NSString *const kEnvironmentLoggingParameter = @"gidenv";

// The name of the query parameter used to log the embedding SDK / wrapper.
NSString *const kSDKWrapperLoggingParameter = @"gidwrapper";

// Supported Apple execution environments
static NSString *const kAppleEnvironmentUnknown = @"unknown";
static NSString *const kAppleEnvironmentIOS = @"ios";
Expand All @@ -34,6 +39,9 @@
static NSString *const kAppleEnvironmentMacOSIOSOnMac = @"macos-ios";
static NSString *const kAppleEnvironmentMacOSMacCatalyst = @"macos-cat";

static NSString *gWrapperIdentifier = nil;
static os_unfair_lock gWrapperIdentifierLock = OS_UNFAIR_LOCK_INIT;

#ifndef GID_SDK_VERSION
#error "GID_SDK_VERSION is not defined: add -DGID_SDK_VERSION=x.x.x to the build invocation."
#endif
Expand All @@ -44,14 +52,45 @@
#define STR(x) STR_EXPAND(x)
#define STR_EXPAND(x) #x

// The prefixed sdk version string to differentiate gid version values used with the legacy gpsdk
// logging key.
NSString* GIDVersion(void) {
// Returns the sanitized form of `candidate`, or nil if it must be dropped.
// Callers must not pass nil.
static NSString * _Nullable GIDSanitizedWrapperIdentifier(NSString *candidate) {
static NSCharacterSet *allowedSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// The range of printable ASCII characters is U+0020 through U+007E inclusive.
allowedSet = [NSCharacterSet characterSetWithRange:NSMakeRange(0x20, 0x5F)];
});

// The drop check happens before truncation: if the original string contains any character
// outside the printable ASCII range, we drop the entire value.
if ([candidate rangeOfCharacterFromSet:[allowedSet invertedSet]].location != NSNotFound) {
return nil;
}

// An empty string is also discarded.
if (candidate.length == 0) {
return nil;
}

// A surviving string longer than 100 characters is truncated to 100.
if (candidate.length > 100) {
// Truncating with -substringToIndex:100 is safe here precisely because the drop check has
// already guaranteed every character is single-unit ASCII, so there is no risk of splitting
// a surrogate pair.
return [candidate substringToIndex:100];
}

return candidate;
}

@implementation GIDSignInPreferences

+ (NSString *)sdkVersion {
return [NSString stringWithFormat:@"gid-%@", @STR(GID_SDK_VERSION)];
}

// Get the current Apple execution environment.
NSString* GIDEnvironment(void) {
+ (NSString *)environment {
NSString *appleEnvironment = kAppleEnvironmentUnknown;

#if TARGET_OS_MACCATALYST
Expand Down Expand Up @@ -80,7 +119,52 @@
return appleEnvironment;
}

@implementation GIDSignInPreferences
+ (nullable NSString *)wrapperIdentifier {
os_unfair_lock_lock(&gWrapperIdentifierLock);
NSString *wrapper = [gWrapperIdentifier copy];
os_unfair_lock_unlock(&gWrapperIdentifierLock);
return wrapper;
}

+ (void)setWrapperIdentifier:(nullable NSString *)wrapperIdentifier {
if (wrapperIdentifier == nil) {
os_unfair_lock_lock(&gWrapperIdentifierLock);
gWrapperIdentifier = nil;
os_unfair_lock_unlock(&gWrapperIdentifierLock);
return;
}

NSString *sanitized = GIDSanitizedWrapperIdentifier(wrapperIdentifier);
if (sanitized == nil) {
#if DEBUG
NSAssert(NO, @"SDK wrapper '%@' rejected: must not be empty and must only contain printable "
@"ASCII characters (U+0020 to U+007E). Value ignored.", wrapperIdentifier);
#endif
return;
}

os_unfair_lock_lock(&gWrapperIdentifierLock);
NSString *current = gWrapperIdentifier;
if (current != nil && ![current isEqualToString:sanitized]) {
os_unfair_lock_unlock(&gWrapperIdentifierLock);
#if DEBUG
NSAssert(NO, @"SDK wrapper already set to '%@'; ignoring '%@'. More than one "
@"wrapper appears to be present.", current, sanitized);
#endif
return;
}
gWrapperIdentifier = [sanitized copy];
os_unfair_lock_unlock(&gWrapperIdentifierLock);
}

+ (void)addLoggingParameters:(NSMutableDictionary<NSString *, NSString *> *)params {
params[kSDKVersionLoggingParameter] = [self sdkVersion];
params[kEnvironmentLoggingParameter] = [self environment];
NSString *wrapper = [self wrapperIdentifier];
if (wrapper != nil) {
params[kSDKWrapperLoggingParameter] = wrapper;
}
}

+ (NSString *)googleAuthorizationServer {
return kLSOServer;
Expand Down
20 changes: 20 additions & 0 deletions GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,26 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) {
/// The active configuration for this instance of `GIDSignIn`.
@property(nonatomic, nullable) GIDConfiguration *configuration;

/// An optional identifier naming the SDK or wrapper that embeds Google Sign-In,
/// reported to Google as a diagnostic parameter for aggregate metrics only; it
/// is never used for authentication or authorization.
///
/// Format: up to 100 printable ASCII characters (U+0020 to U+007E). A longer
/// value is truncated to its first 100 characters. A value containing any
/// non-ASCII character or any ASCII control character is dropped in its
/// entirety, and asserts in debug builds.
///
/// Policy:
/// * Choose one stable name and keep it stable across your releases.
/// * Do NOT encode your version in it; per-release identifiers make aggregate
/// metrics useless.
/// * Never include anything user-specific, app-specific, or identifying.
/// * Register your identifier with Google before shipping it.
///
/// Set this once, before your first sign-in call. The first valid value wins;
/// later differing values are ignored.
@property(nonatomic, nullable) NSString *wrapperIdentifier;

#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST

/// Configures `GIDSignIn` for use.
Expand Down
Loading