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

Implementation with Firebase #89

Closed
sageata opened this issue Jun 2, 2020 · 18 comments
Closed

Implementation with Firebase #89

sageata opened this issue Jun 2, 2020 · 18 comments

Comments

@sageata
Copy link

sageata commented Jun 2, 2020

Hi,

First of all, thank you for all these packages.

This is not an issue, but more of a problem of mine.

I am trying to implement sign_in_with_apple on Android with Firebase Sign-In, but instead of creating my own server, I am trying to use the Firebase auth_handler

final credentials = await SignInWithApple.getAppleIDCredential( scopes: scopes, webAuthenticationOptions: WebAuthenticationOptions( clientId: '[myID]', redirectUri: Uri.parse( 'https://[appId].firebaseapp.com/__/auth/handler', ), ),

I did all the setup required on Apple Developer Account and also on the Firebase side, including the Web bit, such as the Key Id and the Auth Key.

Everything works, however, after I Sign in with Apple and I get redirected to 'https://[appId].firebaseapp.com/__/auth/handler', nothing happens. I would assume that the Browser should close and return to the app and get my credentials, same way it happens on iOS.

Do you have any suggestions on what I should do next in order to make it work with the 'https://[appId].firebaseapp.com/__/auth/handler?

Thank you in advance.

@tp
Copy link
Collaborator

tp commented Jun 2, 2020

Hey @sageata,

I can't offer a working implementation with Firebase here (as we've never tested it specifically), but there have been various tickets around that here (so I assume quite some people are working on/with it).

If anyone got/gets it working, I would like to add a write-up of that to the docs :)

Do you have any suggestions on what I should do next in order to make it work with the 'https://[appId].firebaseapp.com/__/auth/handler?

What the reference server does on the callback URL is to open a deeplink back to the app, which has 2 jobs:

  • Pass the credential data safely to the app
  • Bring the app back to the foreground, hence having the Android system close the Chrome custom tab shown "above" the app's activity

I don't know how the Firebase handler works or what you can configure there, but if opening a deep link back to your app is possible, that is probably the simplest way.

Apart from that, when using Firebase the login could already be successful when the callback is opened in the Custom tab, so do you get any API feedback for that?

Currently there is no way to close the Chrome Custom Tab from this package, but if that is technically possible and you get some notification that the login was successful, closing the Chrome Custom Tab programmatically might be an alternative to the deep link approach in this case.

@sageata
Copy link
Author

sageata commented Jun 2, 2020

Thank you for the reply @tp

Apart from that, when using Firebase the login could already be successful when the callback is >opened in the Custom tab, so do you get any API feedback for that?

I do not get any Feedback from the API, even after closing the Chrome Tab manually.

I thought that the answer might rest with the AndroidManifest intent, however, I am not sure what I could modify in there to adapt for the Firebase Auth Handler.

I will keep searching for an answer.

@sageata
Copy link
Author

sageata commented Jun 2, 2020

I think I got my answer here:

auth.startActivityForSignInWithProvider(this, provider.build()) .addOnSuccessListener { authResult -> // Sign-in successful! Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}") val user = authResult.user // ... } .addOnFailureListener { e -> Log.w(TAG, "activitySignIn:onFailure", e) }

The current FirebaseAuth package for Flutter does not have this function : startActivityForSignInWithProvider, meaning that the Firebase handler is not designed to return the credential and close app. I think I will have to do the Server bit as well after all 😄

@sageata sageata closed this as completed Jun 2, 2020
@HenriBeck
Copy link
Member

@sageata I think there are two options here:

  1. Use the Firebase Android Guide. For this option, you wouldn't need our plugin at all; instead, it should be handled all from Firebase Auth package.
    The benefit would that you don't need the mentioned server part because Firebase will take care of it.

  2. Use the iOS Guide also for Android.
    I believe that the iOS guide should also just work on Android with our plugin when you set up the server correctly. That lets our plugin handle all of the authentications and redirecting, and just giving the user data to Firebase once you are out of our flow.

Let us know if you face any issues, and we would be happy to add some docs about the integration because we did already get a few issues about that specific issue

@sageata
Copy link
Author

sageata commented Jun 2, 2020

@HenriBeck Thank you!

I will start with 1. Time to get my hands dirty in some Kotlin code for the first time 😄

@sageata
Copy link
Author

sageata commented Jun 2, 2020

So, I used the Firebase Guide for Android for Sign in With Apple and I got to this eventually

package my.package

import android.util.Log
import androidx.annotation.NonNull
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.OAuthProvider
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant


class MainActivity : FlutterActivity() {

    private val channel = "channel.name"
    private val TAG = MainActivity::class.java.simpleName

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channel).setMethodCallHandler { call, result ->

            if (call.method == "signInWithApple") {
                val auth = FirebaseAuth.getInstance();
                val provider = OAuthProvider.newBuilder("apple.com")
                provider.setScopes(arrayOf("email", "name").toMutableList());
                val pending = auth.pendingAuthResult;

                if (pending != null) {
                    pending.addOnSuccessListener { authResult ->
                        Log.d(TAG, "checkPending:onSuccess:$authResult")

                    }.addOnFailureListener { e ->
                        Log.w(TAG, "checkPending:onFailure", e)
                    }
                } else {
                    Log.d(TAG, "pending: null")
                    auth.startActivityForSignInWithProvider(this, provider.build()).addOnSuccessListener { authResult ->
                        // Sign-in successful!
                        Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}")
                        val user = authResult.user
                    }
                            .addOnFailureListener { e ->
                                Log.w(TAG, "activitySignIn:onFailure", e)
                            }
                }
                result.success("It worked!")
            } else {
                result.notImplemented();
            }
        }

    }
}

I still have some work to do on the above code.

In Flutter I use this to call the function:

if (Platform.isAndroid) {
  final platform = MethodChannel('channel.name');
  platform.invokeMethod('signInWithApple');
}

channel.name can be anything you choose.

I also set a listener on the FirebaseAuth.instance.onAuthStateChanged and if the user is not null then I hide the Login Page.

Also, very important, in Firebase Console, one needs to set up the SHA-1 Fingerprint, otherwise you will get an error complaining about it.

Check this page on how to get the fingerprint

https://developers.google.com/android/guides/client-auth

@HenriBeck
Copy link
Member

@sageata Then you will most likely need to file a ticket here to get this implemented in Firebase Auth.

@tp
Copy link
Collaborator

tp commented Jun 2, 2020

@sageata If you add this based on top of this library, wouldn't https://firebase.google.com/docs/auth/android/apple#advanced_handle_the_sign-in_flow_manually be the most appropriate?

  1. Generate a nonce
  2. Ask for the token via this plugin
  3. Pass both to Firebase (ideally the API mentioned before is or would be exposed in the Flutter package, so you don't have to have a second native plugin to get the credential to Firebase)

@sageata
Copy link
Author

sageata commented Jun 3, 2020

@tp I didn't know that providing a nonce would make the change the way the plugin works.

I will give it a try.

@sageata
Copy link
Author

sageata commented Jun 3, 2020

@sageata Then you will most likely need to file a ticket here to get this implemented in Firebase Auth.

@HenriBeck Done!

@tp
Copy link
Collaborator

tp commented Jun 3, 2020

For reference: firebase/flutterfire#2691

@ndhbr
Copy link

ndhbr commented Jun 8, 2020

So there is no easy way for the browser to close automatically? Or have I misunderstood something wrong?

@HenriBeck
Copy link
Member

@ndhbr Could you please elaborate? See also #94 which verifies that it is possible to integrate our plugin with Firebase Auth.

@ndhbr
Copy link

ndhbr commented Jun 9, 2020

@HenriBeck

@ndhbr Could you please elaborate? See also #94 which verifies that it is possible to integrate our plugin with Firebase Auth.

I have exactly the same problem as the initial question.

The package works fine under iOS (thanks for that!). And on Android, after login, I am redirected to the RedirectUri as specified. Unfortunately, the browser does not close automatically after that.

So I wanted to ask if there is a simple solution for this or if I should just implement the login for Android manually.

@tp
Copy link
Collaborator

tp commented Jun 9, 2020

So I wanted to ask if there is a simple solution for this or if I should just implement the login for Android manually.

@ndhbr Does your redirect URI call forward further to the app's deeplink? That will cause the Chrome Custom Tab to close (pop) while bringing the Flutter activity back to the foreground.

@ndhbr
Copy link

ndhbr commented Jun 9, 2020

@ndhbr Does your redirect URI call forward further to the app's deeplink? That will cause the Chrome Custom Tab to close (pop) while bringing the Flutter activity back to the foreground.

Unfortunately not. If I understand this correctly, the server should append the parameters, right?

Maybe I should have said that I use Firebase as well. So I have as callback URL only https://[appId].firebaseapp.com/__/auth/handler
without additional parameters.

With Firebase you cannot configure this either. If I close the tab manually nothing happens, so the login was not successful.

firebase

I have implemented the Google Login with another package. Since Apple doesn't have a native Google Login either, it is done via the browser, like the Apple Login on Android, on iOS. But after the login the browser closes and I get the credential in the code.

Unfortunately I don't get the credential here when I close the browser manually because it throws the exception SignInWithAppleAuthorizationException - The user closed the Custom Tab.

@HenriBeck
Copy link
Member

HenriBeck commented Jun 9, 2020

@ndhbr You don't want to use the Firebase Auth callback URL when you integrate it with this package.

For our package to work, you will need this small server component as described in the README. Then we will link back to your app and return you the credentials of the user.
https://github.com/aboutyou/dart_packages/tree/master/packages/sign_in_with_apple#server

Then you can give those credentials to firebase using:

final credential = OAuthProvider(providerId: 'apple.com').getCredential(
    idToken: appleIdCredential.identityToken,
    accessToken: appleIdCredential.authorizationCode,
);

@ndhbr
Copy link

ndhbr commented Jun 9, 2020

For our package to work, you will need this small server component as described in the README. Then we will link back to your app and return you the credentials of the user.
https://github.com/aboutyou/dart_packages/tree/master/packages/sign_in_with_apple#server

@HenriBeck Thanks for the help. I'm gonna try this.

Then you can give those credentials to firebase using:

final credential = OAuthProvider(providerId: 'apple.com').getCredential(
    idToken: appleIdCredential.identityToken,
    accessToken: appleIdCredential.authorizationCode,
);

I have already implemented this, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants