[google_sign_in_web] Ensure not-signed-in users are returned as `null`. (#2649)

The core plugin assumes that a not-authenticated user is being returned as null from the platform code, but the web implementation is currently returning a null-object (as in the design pattern).

This change ensures that not signed-in users are returned as null, not as an object with all its properties set to null!

Co-Authored-By: Harry Terkelsen <hterkelsen@users.noreply.github.com>
diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
index 6f186fd..74d5c8b 100644
--- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.1
+
+* Ensure the web code returns `null` when the user is not signed in, instead of a `null-object` User. Fixes [issue 52338](https://github.com/flutter/flutter/issues/52338).
+
 ## 0.9.0
 
 * Add support for methods introduced in `google_sign_in_platform_interface` 1.1.0.
diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart
index 1016e8e..36bb52d 100644
--- a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart
+++ b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart
@@ -31,9 +31,13 @@
 
 /// Utility method that converts `currentUser` to the equivalent
 /// [GoogleSignInUserData].
+/// This method returns `null` when the [currentUser] is not signed in.
 GoogleSignInUserData gapiUserToPluginUserData(auth2.GoogleUser currentUser) {
-  assert(currentUser != null);
-  final auth2.BasicProfile profile = currentUser.getBasicProfile();
+  final bool isSignedIn = currentUser?.isSignedIn() ?? false;
+  final auth2.BasicProfile profile = currentUser?.getBasicProfile();
+  if (!isSignedIn || profile?.getId() == null) {
+    return null;
+  }
   return GoogleSignInUserData(
     displayName: profile?.getName(),
     email: profile?.getEmail(),
diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml
index 9f2ce26..90dca0e 100644
--- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Flutter plugin for Google Sign-In, a secure authentication system
   for signing in with a Google account on Android, iOS and Web.
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_web
-version: 0.9.0
+version: 0.9.1
 
 flutter:
   plugin:
@@ -25,6 +25,7 @@
     sdk: flutter
   google_sign_in: ^4.0.14
   pedantic: ^1.8.0
+  mockito: ^4.1.1
 
 environment:
   sdk: ">=2.6.0 <3.0.0"
diff --git a/packages/google_sign_in/google_sign_in_web/test/gapi_mocks/src/google_user.dart b/packages/google_sign_in/google_sign_in_web/test/gapi_mocks/src/google_user.dart
index 15993bb..f8e794a 100644
--- a/packages/google_sign_in/google_sign_in_web/test/gapi_mocks/src/google_user.dart
+++ b/packages/google_sign_in/google_sign_in_web/test/gapi_mocks/src/google_user.dart
@@ -23,5 +23,8 @@
   },
   getGrantedScopes: () => 'some scope',
   grant: () => true,
+  isSignedIn: () => {
+    return ${data != null ? 'true' : 'false'};
+  },
 }
 ''';
diff --git a/packages/google_sign_in/google_sign_in_web/test/gapi_utils_test.dart b/packages/google_sign_in/google_sign_in_web/test/gapi_utils_test.dart
new file mode 100644
index 0000000..2dc49fc
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in_web/test/gapi_utils_test.dart
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+@TestOn('browser')
+
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:google_sign_in_web/src/generated/gapiauth2.dart' as gapi;
+import 'package:google_sign_in_web/src/utils.dart';
+import 'package:mockito/mockito.dart';
+
+class MockGoogleUser extends Mock implements gapi.GoogleUser {}
+
+class MockBasicProfile extends Mock implements gapi.BasicProfile {}
+
+void main() {
+  // The non-null use cases are covered by the auth2_test.dart file.
+
+  group('gapiUserToPluginUserData', () {
+    var mockUser;
+
+    setUp(() {
+      mockUser = MockGoogleUser();
+    });
+
+    test('null user -> null response', () {
+      expect(gapiUserToPluginUserData(null), isNull);
+    });
+
+    test('not signed-in user -> null response', () {
+      when(mockUser.isSignedIn()).thenReturn(false);
+      expect(gapiUserToPluginUserData(mockUser), isNull);
+    });
+
+    test('signed-in, but null profile user -> null response', () {
+      when(mockUser.isSignedIn()).thenReturn(true);
+      expect(gapiUserToPluginUserData(mockUser), isNull);
+    });
+
+    test('signed-in, null userId in profile user -> null response', () {
+      when(mockUser.isSignedIn()).thenReturn(true);
+      when(mockUser.getBasicProfile()).thenReturn(MockBasicProfile());
+      expect(gapiUserToPluginUserData(mockUser), isNull);
+    });
+  });
+}