Skip to content

[google-sign-in] Canceled sign-in attempt not reflected by onCurrentUserChanged Stream #161890

@anqit

Description

@anqit

What package does this bug report belong to?

google_sign_in

What target platforms are you seeing this bug on?

iOS, Android

Have you already upgraded your packages?

Yes

Dependency versions

pubspec.lock
  google_identity_services_web:
    dependency: transitive
    description:
      name: google_identity_services_web
      sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
      url: "https://pub.dev"
    source: hosted
    version: "0.3.1+4"
  google_sign_in:
    dependency: "direct main"
    description:
      name: google_sign_in
      sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f"
      url: "https://pub.dev"
    source: hosted
    version: "6.2.1"
  google_sign_in_android:
    dependency: transitive
    description:
      name: google_sign_in_android
      sha256: "0928059d2f0840f63c7b07a30cf73b593ae872cdd0dbd46d1b9ba878d2599c01"
      url: "https://pub.dev"
    source: hosted
    version: "6.1.33"
  google_sign_in_ios:
    dependency: transitive
    description:
      name: google_sign_in_ios
      sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a"
      url: "https://pub.dev"
    source: hosted
    version: "5.7.8"
  google_sign_in_platform_interface:
    dependency: transitive
    description:
      name: google_sign_in_platform_interface
      sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971"
      url: "https://pub.dev"
    source: hosted
    version: "2.4.5"

Steps to reproduce

  1. Make a sign-in request via GoogleSignIn#signIn()
  2. As the app user, cancel the sign-in without choosing a Google account via the UI

Expected results

The stream returned by GoogleSignIn#onCurrentUserChanged() should have a null element emitted for implementations that rely on the stream to handle auth events

Actual results

While the Future returned by GoogleSignIn#signIn() completes with null, the user changed stream does not emit a null value.

Code sample

Code sample
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Sign-in bug demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Google Sign-in bug demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static final _clientId = 'google client id';

  final _googleSignIn = GoogleSignIn(clientId: _clientId);
  bool loading = false;
  bool signedIn = false;

  @override
  void initState() {
    super.initState();

    // relying on changes in the stream to update auth status
    _googleSignIn.onCurrentUserChanged.listen((acc) {
      setState(() {
        loading = false;
        signedIn = acc != null;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (loading)
              ...[
                Text('loading auth state'),
                CircularProgressIndicator(),
              ]
            else if (signedIn)
              ...[
                Text('you\'re signed in!'),
                ElevatedButton(onPressed: _doGoogleSignOut, child: Text('sign out')),
              ]
            else
              ElevatedButton(onPressed: _doGoogleSignIn, child: Text('sign in with google')),
          ],
        ),
      ),
    );
  }

  void _doGoogleSignIn() {
    setState(() {
      loading = true;

      // when a user cancels the auth process, the future returned by the following request will complete with `null`,
      // however, the `_googleSignIn.onCurrentUserChanged` `Stream` does not emit a new `null` value,
      // nor is an error thrown. this means that we can't use a consistent approach (i.e., relying solely on the stream)
      // to handle the canceled auth flow scenario, and have to also await this future and see if the result is null
      _googleSignIn.signIn();
    });
  }

  void _doGoogleSignOut() {
    setState(() {
      loading = true;
      _googleSignIn.signOut();
    });
  }
}

Screenshots or Videos

Screenshots / Video demonstration

https://drive.google.com/file/d/1YoO4xrlAKxRPhnxa7tnkleyRSaxoekmJ/view?usp=sharing

The video first shows that the happy path flows of sign-in and sign-out work when relying on the stream for auth updates. But then, for the Cancel flow, the stream is never updated, so the app remains in the loading state indefinitely.

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Metadata

Metadata

Labels

P2Important issues not at the top of the work listfound in release: 3.27Found to occur in 3.27found in release: 3.28Found to occur in 3.28has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: google_sign_inThe Google Sign-In pluginpackageflutter/packages repository. See also p: labels.r: fixedIssue is closed as already fixed in a newer versionteam-ecosystemOwned by Ecosystem teamtriaged-ecosystemTriaged by Ecosystem team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions