Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: how do I get Apple credential to link an apple account to an account on my firebase app?

The Official Firebase docs say:

...
3.Get a Credential object for the new authentication provider:

// Google Sign-in
final credential = GoogleAuthProvider.credential(idToken: idToken);

// Email and password sign-in
final credential =
    EmailAuthProvider.credential(email: emailAddress, password: password);

// Etc.

4.Pass the Credential object to the sign-in user's linkWithCredential() method:

try {
  final userCredential = await FirebaseAuth.instance.currentUser
      ?.linkWithCredential(credential);
} on FirebaseAuthException catch (e) {
  // ...
}

I have not found any way to get a credential (more specifically, an AuthCredential) for Apple, which is needed as an argument to the function:

FirebaseAuth.instance.currentUser.linkWithCredential(AuthCredential credential)

I have found ways to link Google and Facebook accounts to existing accounts on my firebase app by following the above described methods, just couldn't figure out a way to get that credential object for Apple. Here's what I tried:
For Google, there is:

final credential = GoogleAuthProvider.credential(...);

For Facebook, there is:

final credential = FacebookAuthProvider.credential(...);

I could not find this in any docs, I checked, and yes, for Apple there is a similar function:

String accessToken = ? //how do I get this???
final credential = AppleAuthProvider.credential(accessToken);

The problem is, how do I get this accessToken String, which is required as a parameter? I have searched everywhere, and couldn't find a solution.


The only possibility I see now to link an apple account to an existing account on my app, is to:

  • call the same code that I use for Signing in With Apple
  • and warn the user that he should use "Show My Email" and NOT "Hide My Email", so that this "Sign in With Apple" gets added to the existing firebase account (important: I link accounts that have the same email in my firebase auth settings).
    Because if the user chooses "Hide My Email" instead, a new firebase account will be created with the randomly generated email address (for anonymity) from Apple.
like image 517
Cedric Avatar asked Sep 16 '25 13:09

Cedric


2 Answers

As of Firebase Auth version 4.4.0, you don't need the Apple credential to link an Apple account. Just call the linkWithProvider method instead of linkWithCredential and Firebase will run the Apple sign in flow for you. If you are using Flutter Web, call linkWithPopup.

try {

  //AppleAuthProvider is imported from the firebase_auth package
  var appleProvider = AppleAuthProvider();

  //shows native UI that asks user to show or hide their real email address
  appleProvider.addScope('email'); //this scope is required

  //pulls the user's full name from their Apple account
  appleProvider.addScope('name'); //this is not required

  //runs Apple sign in flow and links the Apple account
  final userCredential = await FirebaseAuth.instance.currentUser
      ?.linkWithProvider(appleProvider); 

} on FirebaseAuthException catch (e) {
  switch (e.code) {
    case "provider-already-linked":
      print("The provider has already been linked to the user.");
      break;
    case "invalid-credential":
      print("The provider's credential is not valid.");
      break;
    case "credential-already-in-use":
      print("The account corresponding to the credential already exists, "
          "or is already linked to a Firebase User.");
      break;
    // See the API reference for the full list of error codes.
    default:
      print("Unknown error.");
  }
}

Unfortunately, You cannot force the user to provide you with their real email address when using Apple sign in. However, it is worth noting that the 'private-relay' email generated by Apple can be treated as a real email address in your app. If the user logs out, deletes your app, or switches devices, as long as they login again using the Apple sign in flow, the same 'private-relay' email will be used. All messages you send to that 'private-relay' email will also be redirected by Apple servers to their real email address.

like image 99
Eric Su Avatar answered Sep 19 '25 04:09

Eric Su


I have used the "the_apple_sign_in" package before. In this package:

  • You can get the access token from the credantial as String.fromCharCodes(appleIdCredential.authorizationCode!).
  • You can get the id token from the credantial as String.fromCharCodes(appleIdCredential!.identityToken!).

Perhaps you can similarly get the Firebase credential. Also here you can find a sample code with this package:

final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<UserModel?> signInWithApple(
      {List<Scope> scopes = const [Scope.email, Scope.fullName]}) async {
    UserModel userModel;
    // 1. perform the sign-in request
    final result = await TheAppleSignIn.performRequests(
        [AppleIdRequest(requestedScopes: scopes)]);
    // 2. check the result
    switch (result.status) {
      case AuthorizationStatus.authorized:
        final appleIdCredential = result.credential;
        final oAuthProvider = OAuthProvider("apple.com");
        final credential = oAuthProvider.credential(
          idToken: String.fromCharCodes(appleIdCredential!.identityToken!),
          accessToken:
              String.fromCharCodes(appleIdCredential.authorizationCode!),
        );
        final authResult = await _auth.signInWithCredential(credential);
        final firebaseUser = authResult.user;
        firebaseUser!.updateDisplayName(
            '${appleIdCredential.fullName!.givenName} ${appleIdCredential.fullName!.familyName}');

        userModel = UserModel(
            uid: firebaseUser.uid,
            name: firebaseUser.displayName,
            email: firebaseUser.email,
            image: firebaseUser.photoURL,
            idToken:
                String.fromCharCodes(appleIdCredential.authorizationCode!));
        return userModel;
      case AuthorizationStatus.error:
        return null;
      case AuthorizationStatus.cancelled:
        return null;
    }
  }
like image 22
Azizoglu Avatar answered Sep 19 '25 04:09

Azizoglu