supabase / supabase-flutter

Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
https://supabase.com/
MIT License
746 stars 184 forks source link

Native OS Google Sign in Support #625

Closed Mikephii closed 10 months ago

Mikephii commented 1 year ago

the current "native" sign in shown in the docs uses appAuth which pushes the user to a browser to sign in. it does not use the native OS sign in for google.

dshukertjr commented 1 year ago

We will keep this issue opened for anyone landing here in the future.

The Google sign-in for iOS does not support providing nonce, which is part of the OIDC spec for implement OIDC connect sign in. Therefore, it currently cannot be used with Supabase auth, which supports OIDC login. The Google login package can be used when nonce support is added to it.

Related: https://github.com/google/GoogleSignIn-iOS/issues/135#issuecomment-1643759758

Mikephii commented 1 year ago

@dshukertjr Just leaving this here for others too.

Ive currently using google_sign_in 6.1.5 on android only as the idToken returned doesnt include a nonce. So i can support native OS sign-in for google on android.

The only trick is that you need to create a web oAuth credential instead of an android credential and store it in res/values/strings.xml. GoogleSignIn then pulls this as a serverId instead of a clientId 🤷‍♂️

followed this fix from this issue

for ios im using the appAuth method described in the supabase docs, this pushes user to a browser to sign in, but ios doesnt have native OS google sign in anyway so nothing lost 👍

im a full newbie to flutter and supabase so unsure if this will cause me any issues but so far so good.

volkankoc commented 1 year ago

Supabase flutter native google sigin in error

I/flutter ( 4947): ***** SupabaseDeepLinkingMixin startAuthObserver D/EGL_emulation( 4947): app_time_stats: avg=1049.24ms min=82.09ms max=2016.39ms count=2 E/OpenGLRenderer( 4947): Unable to match the desired swap behavior. D/CompatibilityChangeReporter( 4947): Compat change id reported: 78294722; UID 10192; state: ENABLED E/flutter ( 4947): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null, null) E/flutter ( 4947): #0 GoogleSignInApi.signIn (package:google_sign_in_android/src/messages.g.dart:221:7) E/flutter ( 4947): E/flutter ( 4947): #1 GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:278:30) E/flutter ( 4947): E/flutter ( 4947): #2 GoogleSignIn.signIn.isCanceled (package:google_sign_in/google_sign_in.dart:431:5) E/flutter ( 4947): E/flutter ( 4947): W/WindowOnBackDispatcher( 4947): sendCancelIfRunning: isInProgress=falsecallback=android.view.ViewRootImpl$$ExternalSyntheticLambda17@f4fc7c3 D/EGL_emulation( 4947): app_time_stats: avg=250.41ms min=1.36ms max=4805.27ms count=21 W/mple.auth_test2( 4947): Cleared Reference was only reachable from finalizer (only reported once)

DanMossa commented 1 year ago

I'm using Native Google Sign in like this

Future<MessageOrAuthUser> signInWithGoogle() async {
  GoogleSignIn googleSignIn = GoogleSignIn(
    clientId: constants.WEB_GOOGLE_CLIENT_ID,
    scopes: ['email'],
  );

  await googleSignIn.signInSilently();
  GoogleSignInAccount? account = await googleSignIn.signIn();
  GoogleSignInAuthentication? auth;
  if (account != null) {
    auth = await account.authentication;
  }

  AuthResponse? signInRes;
  AuthException? error;
  if (auth?.idToken != null) {
    try {
      signInRes = await AuthService.signInWIthOpenID(
        idToken: auth!.idToken!,
        provider: Provider.google,
      );
    } on AuthException catch (e) {
      error = e;
    }
  }

  return _handleSignInResponse(signInRes, error,
      firstName: account?.displayName?.split(' ').elementAtOrNull(0));
}
MessageOrAuthUser _handleSignInResponse(
  AuthResponse? signInRes,
  AuthException? error, {
  required String? firstName,
}) {
  User? resUser = signInRes?.user;
  String? resErrorMessage = error?.message;

  if (resUser != null && resErrorMessage == null) {
    AuthUserWithName authUserWithName = AuthUserWithName(resUser, firstName: firstName);
    return MessageOrAuthUser(authUser: authUserWithName);
  }

  if (resErrorMessage == null && resUser == null) {
    return MessageOrAuthUser(
      message: Message(
          title: "Unexpected error", body: "Received an unexpected error. Please contact support."),
    );
  }

  Message? titleAndMessage;
  switch (resErrorMessage) {
    case "Invalid login credentials":
      titleAndMessage = Message(
          title: "Invalid login credentials",
          body: "The email or password you entered is incorrect. Please try again.");
      break;

    case "Email not confirmed":
      titleAndMessage = Message(
          title: "Email not confirmed",
          body: "Tap the link sent to your email address to verify your account.");
      break;

    case 'duplicate key value violates unique constraint "users_email_partial_key"':
    case 'duplicate key value violates unique constraint "users_email_key"':
      titleAndMessage = Message(
        title: "Account already exists",
        body: "Account already exists with email and password.",
      );
      break;

    case "Database error saving new user":
      titleAndMessage = Message(
          title: "Unable to sign in",
          body: "Account may already be created with email and password.");
      break;

    case "invalid id token grant":
      // This means the user has been banned.
      titleAndMessage = Message(
        title: "Unable to sign in",
        body: "Please try again later.",
      );
      break;

    default:
      titleAndMessage = Message(
        title: "Unexpected error",
        body: resErrorMessage ?? "Unknown error. Please try again.",
      );
      break;
  }

  return MessageOrAuthUser(message: titleAndMessage);
}
volkankoc commented 1 year ago

GoogleSignIn googleSignIn = GoogleSignIn( clientId: constants.WEB_GOOGLE_CLIENT_ID, scopes: ['email'], ); Is it necessary to add the web client ID here or separately for Android and iOS?

It's not working I'm still getting the same error

dshukertjr commented 1 year ago

@volkankoc com.google.android.gms.common.api.ApiException: 10 error indicates that one of the configurations is not done correctly. Google does not make it easy to figure out what exactly is wrong as they don't provide any logging anywhere, so unfortunately it is going to be a tedious process of checking what might have gone wrong.

Is it necessary to add the web client ID here or separately for Android and iOS?

First of all, currently in order to perform Google sign in on iOS with Supabase, you cannot use the google_sign_in package, and you have to use flutter_appauth to perform the login as explained here.

On Android, you need to register a Android client ID and web client ID in your Google cloud console. Make sure you have registered the correct SHA1 certificate fingerprint and have provided the correct application ID when registering the Android client ID. When performing Google sign in on Android, you provide the web client ID as the clientId(or serverClientId) in your GoogleSignIn instance. Although we need to register an Android client ID, we don't ever use it in the application.

// Performing Google sign in on Android
GoogleSignIn googleSignIn = GoogleSignIn(
  clientId: constants.WEB_GOOGLE_CLIENT_ID,
  scopes: ['email'],
);
volkankoc commented 1 year ago

My problem solved. Thanks

dshukertjr commented 10 months ago

Supabase now supports Google login using the google_sign_in package.

https://supabase.com/docs/guides/auth/social-login/auth-google?platform=flutter