Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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: 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 @@
## 7.1.0

* Adds support for the `clearAuthorizationToken` method.

## 7.0.5

* Adds support for `hostedDomain` when authenticating.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ dependencies {
implementation 'androidx.credentials:credentials:1.5.0'
implementation 'androidx.credentials:credentials-play-services-auth:1.5.0'
implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.1'
implementation 'com.google.android.gms:play-services-auth:21.3.0'
implementation 'com.google.android.gms:play-services-auth:21.4.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:5.2.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.android.gms.auth.api.identity.AuthorizationClient;
import com.google.android.gms.auth.api.identity.AuthorizationRequest;
import com.google.android.gms.auth.api.identity.AuthorizationResult;
import com.google.android.gms.auth.api.identity.ClearTokenRequest;
import com.google.android.gms.auth.api.identity.Identity;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Scope;
Expand Down Expand Up @@ -219,7 +220,7 @@ public void getCredential(
return;
}

// getCredentialAsync requires an acitivity context, not an application context, per
// getCredentialAsync requires an activity context, not an application context, per
// the API docs.
Activity activity = getActivity();
if (activity == null) {
Expand Down Expand Up @@ -337,17 +338,31 @@ public void clearCredentialState(@NonNull Function1<? super Result<Unit>, Unit>
new CredentialManagerCallback<>() {
@Override
public void onResult(Void result) {
ResultUtilsKt.completeWithClearCredentialStateSuccess(callback);
ResultUtilsKt.completeWithUnitSuccess(callback);
}

@Override
public void onError(@NonNull ClearCredentialException e) {
ResultUtilsKt.completeWithClearCredentialStateError(
ResultUtilsKt.completeWithUnitError(
callback, new FlutterError("Clear Failed", e.getMessage(), null));
}
});
}

@Override
public void clearAuthorizationToken(
@NonNull String token, @NonNull Function1<? super Result<Unit>, Unit> callback) {
authorizationClientFactory
.create(context)
.clearToken(ClearTokenRequest.builder().setToken(token).build())
.addOnSuccessListener(unused -> ResultUtilsKt.completeWithUnitSuccess(callback))
.addOnFailureListener(
e ->
ResultUtilsKt.completeWithUnitError(
callback,
new FlutterError("clearAuthorizationToken failed", e.getMessage(), null)));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error code "clearAuthorizationToken failed" is rather long and descriptive for an error code. It's more conventional to use a short, machine-readable string (e.g., in snake_case or kebab-case). This would improve consistency with common error handling patterns and make it easier for clients to programmatically handle this specific error.

For instance, clearCredentialState uses "Clear Failed", which is shorter. A more structured code like clear_authorization_token_failed would be even better for consistency and parsing.

Suggested change
new FlutterError("clearAuthorizationToken failed", e.getMessage(), null)));
new FlutterError("clear_authorization_token_failed", e.getMessage(), null)));

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we ever start seeing this in practice, this should be switched over to using structured errors anyway; we don't want people doing programatic handling of error code strings that are already inconsistent within and across platform implementations (which is why the structured error guidance exists now). So I didn't worry about making this match anything.

}

@Override
public void authorize(
@NonNull PlatformAuthorizationRequest params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,8 @@ interface GoogleSignInApi {
)
/** Clears CredentialManager credential state. */
fun clearCredentialState(callback: (Result<Unit>) -> Unit)
/** Clears the authorization cache for the given token. */
fun clearAuthorizationToken(token: String, callback: (Result<Unit>) -> Unit)
/** Requests authorization tokens via AuthorizationClient. */
fun authorize(
params: PlatformAuthorizationRequest,
Expand Down Expand Up @@ -692,6 +694,29 @@ interface GoogleSignInApi {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.google_sign_in_android.GoogleSignInApi.clearAuthorizationToken$separatedMessageChannelSuffix",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val tokenArg = args[0] as String
api.clearAuthorizationToken(tokenArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
reply.reply(MessagesPigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ fun completeWithGetCredentialFailure(
callback(Result.success(failure))
}

fun completeWithClearCredentialStateSuccess(callback: (Result<Unit>) -> Unit) {
fun completeWithUnitSuccess(callback: (Result<Unit>) -> Unit) {
callback(Result.success(Unit))
}

fun completeWithClearCredentialStateError(callback: (Result<Unit>) -> Unit, failure: FlutterError) {
fun completeWithUnitError(callback: (Result<Unit>) -> Unit, failure: FlutterError) {
callback(Result.failure(failure))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.google.android.gms.auth.api.identity.AuthorizationClient;
import com.google.android.gms.auth.api.identity.AuthorizationRequest;
import com.google.android.gms.auth.api.identity.AuthorizationResult;
import com.google.android.gms.auth.api.identity.ClearTokenRequest;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.OnSuccessListener;
Expand Down Expand Up @@ -71,6 +72,7 @@ public class GoogleSignInTest {
@Mock CustomCredential mockGenericCredential;
@Mock GoogleIdTokenCredential mockGoogleCredential;
@Mock Task<AuthorizationResult> mockAuthorizationTask;
@Mock Task<Void> mockClearTokenTask;

private GoogleSignInPlugin flutterPlugin;
// Technically this is not the plugin, but in practice almost all of the functionality is in this
Expand All @@ -88,6 +90,8 @@ public void setUp() {
.thenReturn(GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL);
when(mockAuthorizationTask.addOnSuccessListener(any())).thenReturn(mockAuthorizationTask);
when(mockAuthorizationTask.addOnFailureListener(any())).thenReturn(mockAuthorizationTask);
when(mockClearTokenTask.addOnSuccessListener(any())).thenReturn(mockClearTokenTask);
when(mockClearTokenTask.addOnFailureListener(any())).thenReturn(mockClearTokenTask);
when(mockAuthorizationIntent.getIntentSender()).thenReturn(mockAuthorizationIntentSender);
when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity);

Expand Down Expand Up @@ -1144,4 +1148,29 @@ public void clearCredentialState_reportsFailure() {

callbackCaptor.getValue().onError(mock(ClearCredentialException.class));
}

@Test
public void clearAuthorizationToken_callsClient() {
final String testToken = "testToken";
when(mockAuthorizationClient.clearToken(any())).thenReturn(mockClearTokenTask);
plugin.clearAuthorizationToken(
testToken,
ResultCompat.asCompatCallback(
reply -> {
return null;
}));

ArgumentCaptor<ClearTokenRequest> authRequestCaptor =
ArgumentCaptor.forClass(ClearTokenRequest.class);
verify(mockAuthorizationClient).clearToken(authRequestCaptor.capture());

@SuppressWarnings("unchecked")
ArgumentCaptor<OnSuccessListener<Void>> callbackCaptor =
ArgumentCaptor.forClass(OnSuccessListener.class);
verify(mockClearTokenTask).addOnSuccessListener(callbackCaptor.capture());
callbackCaptor.getValue().onSuccess(null);

ClearTokenRequest request = authRequestCaptor.getValue();
assertEquals(testToken, request.getToken());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
google_sign_in_platform_interface: ^3.0.0
google_sign_in_platform_interface: ^3.1.0
http: ">=0.13.0 <2.0.0"

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class GoogleSignInAndroid extends GoogleSignInPlatform {
GoogleSignInPlatform.instance = GoogleSignInAndroid();
}

@override
Future<void> clearAuthorizationToken(ClearAuthorizationTokenParams params) {
return _hostApi.clearAuthorizationToken(params.accessToken);
}

@override
Future<void> init(InitParameters params) async {
_hostedDomain = params.hostedDomain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,34 @@ class GoogleSignInApi {
}
}

/// Clears the authorization cache for the given token.
Future<void> clearAuthorizationToken(String token) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.google_sign_in_android.GoogleSignInApi.clearAuthorizationToken$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[token],
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

/// Requests authorization tokens via AuthorizationClient.
Future<AuthorizeResult> authorize(
PlatformAuthorizationRequest params, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ abstract class GoogleSignInApi {
@async
void clearCredentialState();

/// Clears the authorization cache for the given token.
@async
void clearAuthorizationToken(String token);

/// Requests authorization tokens via AuthorizationClient.
@async
AuthorizeResult authorize(
Expand Down
4 changes: 2 additions & 2 deletions packages/google_sign_in/google_sign_in_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: google_sign_in_android
description: Android implementation of the google_sign_in plugin.
repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
version: 7.0.5
version: 7.1.0

environment:
sdk: ^3.7.0
Expand All @@ -20,7 +20,7 @@ flutter:
dependencies:
flutter:
sdk: flutter
google_sign_in_platform_interface: ^3.0.0
google_sign_in_platform_interface: ^3.1.0

dev_dependencies:
build_runner: ^2.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ class MockGoogleSignInApi extends _i1.Mock implements _i2.GoogleSignInApi {
)
as _i4.Future<void>);

@override
_i4.Future<void> clearAuthorizationToken(String? token) =>
(super.noSuchMethod(
Invocation.method(#clearAuthorizationToken, [token]),
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
)
as _i4.Future<void>);

@override
_i4.Future<_i2.AuthorizeResult> authorize(
_i2.PlatformAuthorizationRequest? params, {
Expand Down
3 changes: 2 additions & 1 deletion packages/google_sign_in/google_sign_in_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 6.2.0

* Adds support for the `clearAuthorizationToken` method.
* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.

## 6.1.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
google_sign_in_platform_interface: ^3.0.0
google_sign_in_platform_interface: ^3.1.0
http: ">=0.13.0 <2.0.0"

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ class GoogleSignInIOS extends GoogleSignInPlatform {
: ServerAuthorizationTokenData(serverAuthCode: serverAuthCode);
}

@override
Future<void> clearAuthorizationToken(
ClearAuthorizationTokenParams params,
) async {
// No-op; the iOS SDK handles token invalidation internally.
}

Future<({String? accessToken, String? serverAuthCode})>
_getAuthorizationTokens(AuthorizationRequestDetails request) async {
String? userId = request.userId;
Expand Down
4 changes: 2 additions & 2 deletions packages/google_sign_in/google_sign_in_ios/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: google_sign_in_ios
description: iOS implementation of the google_sign_in plugin.
repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_ios
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
version: 6.1.0
version: 6.2.0

environment:
sdk: ^3.7.0
Expand All @@ -24,7 +24,7 @@ flutter:
dependencies:
flutter:
sdk: flutter
google_sign_in_platform_interface: ^3.0.0
google_sign_in_platform_interface: ^3.1.0

dev_dependencies:
build_runner: ^2.4.6
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,14 @@ void main() {
verifyInOrder(<Future<void>>[mockApi.disconnect(), mockApi.signOut()]);
});

test('clearAuthorizationToken no-ops without error', () async {
await googleSignIn.clearAuthorizationToken(
const ClearAuthorizationTokenParams(accessToken: 'any token'),
);

verifyZeroInteractions(mockApi);
});

// Returning null triggers the app-facing package to create stream events,
// per GoogleSignInPlatform docs, so it's important that this returns null
// unless the platform implementation is changed to create all necessary
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.4.5 from annotations
// Mocks generated by Mockito 5.4.6 from annotations
// in google_sign_in_ios/test/google_sign_in_ios_test.dart.
// Do not manually edit this file.

Expand Down
3 changes: 2 additions & 1 deletion packages/google_sign_in/google_sign_in_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 1.1.0

* Adds support for the `clearAuthorizationToken` method.
* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.

## 1.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,26 @@ void main() {
);
});
});

group('clearAuthorizationToken', () {
setUp(() {
plugin.init(options);
});

testWidgets('calls clearAuthorizationToken on GIS client', (_) async {
const String someToken = 'someToken';
await plugin.clearAuthorizationToken(
const ClearAuthorizationTokenParams(accessToken: someToken),
);

final List<Object?> arguments =
mockito
.verify(mockGis.clearAuthorizationToken(mockito.captureAny))
.captured;

expect(arguments.first, someToken);
});
});
});

group('userDataEvents', () {
Expand Down
Loading