Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[google_sign_in] Add Android account name field as optional #8573

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1914008
Add a sign-in field to allow Android clients to explicitly specify an…
neilself Feb 5, 2025
bdfe492
Update the changelog.
neilself Feb 5, 2025
bedfeeb
Merge branch 'android_account_name' of ../flutter into android_accoun…
neilself Feb 5, 2025
ccd527a
Minor updates to fix broken checks/tests.
neilself Feb 6, 2025
27f8b32
Fix string formatting in GoogleSignInTest.
neilself Feb 6, 2025
6c71c01
Fix additional string formatting in GoogleSignInTest.
neilself Feb 6, 2025
9265495
Format files that needed formatting.
neilself Feb 6, 2025
97ada7f
Additional formatting fixes for java files.
neilself Feb 6, 2025
596cf34
Specify NDK version according to checks.
neilself Feb 7, 2025
fc18808
Update GoogleSignInTest to fix a broken test.
neilself Feb 8, 2025
1e57cc2
Update GoogleSignInTest.init_PassesForceAccountName() to use MockedCo…
neilself Feb 12, 2025
c9b1f5a
Merge branch 'main' into android_account_name
neilself Feb 12, 2025
2e91e38
Fix missing import of Account.
neilself Feb 12, 2025
c256020
Merge remote-tracking branch 'origin/android_account_name' into andro…
neilself Feb 12, 2025
923d2da
Update formatting within GoogleSignInTest.
neilself Feb 12, 2025
c00f5d3
Override dependencies in extension_google_sign_in_as_googleapis_auth …
neilself Feb 12, 2025
80b054a
Override dependency in extension_google_sign_in_as_googleapis_auth du…
neilself Feb 13, 2025
964bb35
Merge branch 'main' into android_account_name
neilself Feb 13, 2025
68a2032
Override dependency in extension_google_sign_in_as_googleapis_auth/ex…
neilself Feb 13, 2025
9327b58
Update version/CHANGELOG for google_sign_in_ios and extension_google_…
neilself Feb 13, 2025
914829a
Merge branch 'main' into android_account_name
neilself Feb 13, 2025
41e0456
Merge branch 'main' into android_account_name
neilself Feb 14, 2025
802641e
Merge branch 'main' into android_account_name
neilself Feb 19, 2025
6d4c314
Update isNullOrEmpty() usage to match new convention.
neilself Feb 20, 2025
473b5e7
Revert changes to extension_google_sign_in_as_googleapis_auth package.
neilself Feb 21, 2025
c4fd24c
Minor updates to respond to PR review comments.
neilself Feb 21, 2025
16438e2
Revert changes in the extension_google_sign_in_as_googleapis_auth pac…
neilself Feb 21, 2025
1acd588
Revert changes in method_channel_google_sign_in.dart and its test file.
neilself Feb 21, 2025
18af989
Updates the CHANGELOG for google_sign_in_android to better follow the…
neilself Feb 21, 2025
99a101f
Merge branch 'main' into android_account_name
neilself Feb 21, 2025
9476549
Update exception type in google_sign_in_ios_test to match new excepti…
neilself Feb 21, 2025
c694668
Merge branch 'main' into android_account_name
neilself Feb 21, 2025
21282bd
Merge branch 'main' into android_account_name
neilself Feb 25, 2025
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
4 changes: 3 additions & 1 deletion packages/google_sign_in/google_sign_in/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT
## 6.3.0

* Adds a sign-in field to allow Android clients to explicitly specify an account name. This
capability is only available within Android for the underlying libraries.
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.

## 6.2.2
Expand Down
5 changes: 5 additions & 0 deletions packages/google_sign_in/google_sign_in/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ dev_dependencies:

flutter:
uses-material-design: true

# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
{google_sign_in_android: {path: ../../../../packages/google_sign_in/google_sign_in_android}, google_sign_in_ios: {path: ../../../../packages/google_sign_in/google_sign_in_ios}, google_sign_in_platform_interface: {path: ../../../../packages/google_sign_in/google_sign_in_platform_interface}}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class GoogleSignIn {
this.clientId,
this.serverClientId,
this.forceCodeForRefreshToken = false,
this.forceAccountName,
}) {
// Start initializing.
if (kIsWeb) {
Expand Down Expand Up @@ -263,6 +264,9 @@ class GoogleSignIn {
/// Force the authorization code to be valid for a refresh token every time. Only needed on Android.
final bool forceCodeForRefreshToken;

/// Explicitly specifies the account name to be used in sign-in. Must only be set on Android.
final String? forceAccountName;

final StreamController<GoogleSignInAccount?> _currentUserController =
StreamController<GoogleSignInAccount?>.broadcast();

Expand Down Expand Up @@ -317,6 +321,7 @@ class GoogleSignIn {
clientId: clientId,
serverClientId: serverClientId,
forceCodeForRefreshToken: forceCodeForRefreshToken,
forceAccountName: forceAccountName,
));

unawaited(GoogleSignInPlatform.instance.userDataEvents
Expand Down
7 changes: 6 additions & 1 deletion packages/google_sign_in/google_sign_in/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system
for signing in with a Google account.
repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
version: 6.2.2
version: 6.3.0

environment:
sdk: ^3.4.0
Expand Down Expand Up @@ -49,3 +49,8 @@ false_secrets:
- /example/ios/RunnerTests/GoogleService-Info.plist
- /example/ios/RunnerTests/GoogleSignInTests.m
- /example/macos/Runner/Info.plist

# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
{google_sign_in_android: {path: ../../../packages/google_sign_in/google_sign_in_android}, google_sign_in_ios: {path: ../../../packages/google_sign_in/google_sign_in_ios}, google_sign_in_platform_interface: {path: ../../../packages/google_sign_in/google_sign_in_platform_interface}}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ void main() {
verify(mockPlatform.signIn());
});

test('forceAccountName sent with init method call', () async {
final GoogleSignIn googleSignIn =
GoogleSignIn(forceAccountName: 'fakeEmailAddress@example.com');

await googleSignIn.signIn();

_verifyInit(mockPlatform,
forceAccountName: 'fakeEmailAddress@example.com');
verify(mockPlatform.signIn());
});

test('signOut', () async {
final GoogleSignIn googleSignIn = GoogleSignIn();

Expand Down Expand Up @@ -447,6 +458,7 @@ void _verifyInit(
String? clientId,
String? serverClientId,
bool forceCodeForRefreshToken = false,
String? forceAccountName,
}) {
verify(mockSignIn.initWithParams(argThat(
isA<SignInInitParameters>()
Expand Down Expand Up @@ -479,6 +491,11 @@ void _verifyInit(
(SignInInitParameters p) => p.forceCodeForRefreshToken,
'forceCodeForRefreshToken',
forceCodeForRefreshToken,
)
.having(
(SignInInitParameters p) => p.forceAccountName,
'forceAccountName',
forceAccountName,
),
)));
}
4 changes: 4 additions & 0 deletions packages/google_sign_in/google_sign_in_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 6.2.0

* Adds a sign-in field to allow clients to explicitly specify an account name.

## 6.1.35

* Removes the dependency on the Guava library.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ public void init(@NonNull Messages.InitParams params) {
optionsBuilder.setHostedDomain(params.getHostedDomain());
}

String forceAccountName = params.getForceAccountName();
if (!isNullOrEmpty(forceAccountName)) {
optionsBuilder.setAccountName(forceAccountName);
}

signInClient = googleSignInWrapper.getClient(context, optionsBuilder.build());
} catch (Exception e) {
throw new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ public void setForceCodeForRefreshToken(@NonNull Boolean setterArg) {
this.forceCodeForRefreshToken = setterArg;
}

private @Nullable String forceAccountName;

public @Nullable String getForceAccountName() {
return forceAccountName;
}

public void setForceAccountName(@Nullable String setterArg) {
this.forceAccountName = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
InitParams() {}

Expand All @@ -173,13 +183,20 @@ public boolean equals(Object o) {
&& Objects.equals(hostedDomain, that.hostedDomain)
&& Objects.equals(clientId, that.clientId)
&& Objects.equals(serverClientId, that.serverClientId)
&& forceCodeForRefreshToken.equals(that.forceCodeForRefreshToken);
&& forceCodeForRefreshToken.equals(that.forceCodeForRefreshToken)
&& Objects.equals(forceAccountName, that.forceAccountName);
}

@Override
public int hashCode() {
return Objects.hash(
scopes, signInType, hostedDomain, clientId, serverClientId, forceCodeForRefreshToken);
scopes,
signInType,
hostedDomain,
clientId,
serverClientId,
forceCodeForRefreshToken,
forceAccountName);
}

public static final class Builder {
Expand Down Expand Up @@ -232,6 +249,14 @@ public static final class Builder {
return this;
}

private @Nullable String forceAccountName;

@CanIgnoreReturnValue
public @NonNull Builder setForceAccountName(@Nullable String setterArg) {
this.forceAccountName = setterArg;
return this;
}

public @NonNull InitParams build() {
InitParams pigeonReturn = new InitParams();
pigeonReturn.setScopes(scopes);
Expand All @@ -240,19 +265,21 @@ public static final class Builder {
pigeonReturn.setClientId(clientId);
pigeonReturn.setServerClientId(serverClientId);
pigeonReturn.setForceCodeForRefreshToken(forceCodeForRefreshToken);
pigeonReturn.setForceAccountName(forceAccountName);
return pigeonReturn;
}
}

@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<>(6);
ArrayList<Object> toListResult = new ArrayList<>(7);
toListResult.add(scopes);
toListResult.add(signInType);
toListResult.add(hostedDomain);
toListResult.add(clientId);
toListResult.add(serverClientId);
toListResult.add(forceCodeForRefreshToken);
toListResult.add(forceAccountName);
return toListResult;
}

Expand All @@ -270,6 +297,8 @@ ArrayList<Object> toList() {
pigeonResult.setServerClientId((String) serverClientId);
Object forceCodeForRefreshToken = pigeonVar_list.get(5);
pigeonResult.setForceCodeForRefreshToken((Boolean) forceCodeForRefreshToken);
Object forceAccountName = pigeonVar_list.get(6);
pigeonResult.setForceAccountName((String) forceAccountName);
return pigeonResult;
}
}
Expand Down Expand Up @@ -512,6 +541,7 @@ public interface Result<T> {
/** Failure case callback method for handling errors. */
void error(@NonNull Throwable error);
}

/** Asynchronous error handling return type for nullable API method returns. */
public interface NullableResult<T> {
/** Success case callback method for handling returns. */
Expand All @@ -520,6 +550,7 @@ public interface NullableResult<T> {
/** Failure case callback method for handling errors. */
void error(@NonNull Throwable error);
}

/** Asynchronous error handling return type for void API method returns. */
public interface VoidResult {
/** Success case callback method for handling returns. */
Expand All @@ -528,33 +559,43 @@ public interface VoidResult {
/** Failure case callback method for handling errors. */
void error(@NonNull Throwable error);
}

/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface GoogleSignInApi {
/** Initializes a sign in request with the given parameters. */
void init(@NonNull InitParams params);

/** Starts a silent sign in. */
void signInSilently(@NonNull Result<UserData> result);

/** Starts a sign in with user interaction. */
void signIn(@NonNull Result<UserData> result);

/** Requests the access token for the current sign in. */
void getAccessToken(
@NonNull String email, @NonNull Boolean shouldRecoverAuth, @NonNull Result<String> result);

/** Signs out the current user. */
void signOut(@NonNull VoidResult result);

/** Revokes scope grants to the application. */
void disconnect(@NonNull VoidResult result);

/** Returns whether the user is currently signed in. */
@NonNull
Boolean isSignedIn();

/** Clears the authentication caching for the given token, requiring a new sign in. */
void clearAuthCache(@NonNull String token);

/** Requests access to the given scopes. */
void requestScopes(@NonNull List<String> scopes, @NonNull Result<Boolean> result);

/** The codec used by GoogleSignInApi. */
static @NonNull MessageCodec<Object> getCodec() {
return PigeonCodec.INSTANCE;
}

/**
* Sets up an instance of `GoogleSignInApi` to handle messages through the `binaryMessenger`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
Expand All @@ -32,6 +33,8 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

Expand Down Expand Up @@ -284,6 +287,25 @@ public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdFromResourc
initAndAssertForceCodeForRefreshToken(params, true);
}

@Test
public void init_PassesForceAccountName() {
String fakeAccountName = "fakeEmailAddress@example.com";

try (MockedConstruction<Account> mocked =
Mockito.mockConstruction(
Account.class,
(mock, context) -> {
when(mock.toString()).thenReturn(fakeAccountName);
})) {
InitParams params = buildInitParams("fakeClientId", "fakeServerClientId2", fakeAccountName);

initAndAssertForceAccountName(params, fakeAccountName);

List<Account> constructed = mocked.constructed();
Assert.assertEquals(1, constructed.size());
}
}

public void initAndAssertServerClientId(InitParams params, String serverClientId) {
ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
ArgumentCaptor.forClass(GoogleSignInOptions.class);
Expand All @@ -304,9 +326,23 @@ public void initAndAssertForceCodeForRefreshToken(
forceCodeForRefreshToken, optionsCaptor.getValue().isForceCodeForRefreshToken());
}

public void initAndAssertForceAccountName(InitParams params, String forceAccountName) {
ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
ArgumentCaptor.forClass(GoogleSignInOptions.class);
when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture()))
.thenReturn(mockClient);
plugin.init(params);
Assert.assertEquals(forceAccountName, optionsCaptor.getValue().getAccount().toString());
}

private static InitParams buildInitParams(String clientId, String serverClientId) {
return buildInitParams(
Messages.SignInType.STANDARD, Collections.emptyList(), clientId, serverClientId, false);
Messages.SignInType.STANDARD,
Collections.emptyList(),
clientId,
serverClientId,
false,
null);
}

private static InitParams buildInitParams(
Expand All @@ -316,15 +352,28 @@ private static InitParams buildInitParams(
Collections.emptyList(),
clientId,
serverClientId,
forceCodeForRefreshToken);
forceCodeForRefreshToken,
null);
}

private static InitParams buildInitParams(
String clientId, String serverClientId, String forceAccountName) {
return buildInitParams(
Messages.SignInType.STANDARD,
Collections.emptyList(),
clientId,
serverClientId,
false,
forceAccountName);
}

private static InitParams buildInitParams(
Messages.SignInType signInType,
List<String> scopes,
String clientId,
String serverClientId,
boolean forceCodeForRefreshToken) {
boolean forceCodeForRefreshToken,
String forceAccountName) {
InitParams.Builder builder = new InitParams.Builder();
builder.setSignInType(signInType);
builder.setScopes(scopes);
Expand All @@ -335,6 +384,9 @@ private static InitParams buildInitParams(
builder.setServerClientId(serverClientId);
}
builder.setForceCodeForRefreshToken(forceCodeForRefreshToken);
if (forceAccountName != null) {
builder.setForceAccountName(forceAccountName);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ dev_dependencies:

flutter:
uses-material-design: true

# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
{google_sign_in_platform_interface: {path: ../../../../packages/google_sign_in/google_sign_in_platform_interface}}
Loading