firebase-ios-sdk icon indicating copy to clipboard operation
firebase-ios-sdk copied to clipboard

auth.useUserAccessGroup(accessGroup) does not work to share authentication between Watch and iPhone

Open RohitRajendran opened this issue 3 years ago • 7 comments

[REQUIRED] Step 1: Describe your environment

  • Xcode version: Version 12.4
  • Firebase SDK version: 7.9.1
  • Installation method: Swift Package Manager
  • Firebase Component: Auth

[REQUIRED] Step 2: Describe the problem

Steps to reproduce:

I'm trying to use Auth.auth().useUserAccessGroups across both iPhone and WatchOS apps to share authentication. The iPhone app initiates an anonymous sign in and should be sharing the user but the watch app returns nil.

  1. Setup an iPhone and WatchOS app and follow the following steps for cross app authentication https://firebase.google.com/docs/auth/ios/single-sign-on?authuser=0. I set up an app group and used that in both the iphone and watch app.
  2. In the iphone app, on init, utilize signInAnonymously after calling Auth.auth().useUserAccessGroup
  3. In the watch os app, on init, check the currentUser value after calling Auth.auth().useUserAccessGroup
  4. Run the iphone app and then the watch os app. The watch os app will return nil for user

Relevant Code:

In iPhone app:

init() {
        FirebaseApp.configure()

        do {
            Auth.auth().shareAuthStateAcrossDevices = true
            try Auth.auth().useUserAccessGroup("APP_GROUP_NAME")
        } catch let error as NSError {
            print("Error changing user access group: %@", error)
        }

        if Auth.auth().currentUser === nil {
            Auth.auth().signInAnonymously()
        }
        
        print(Auth.auth().currentUser?.uid, "user")
    }

In watch app:

init() {
        FirebaseApp.configure()

        do {
            Auth.auth().shareAuthStateAcrossDevices = true
            try Auth.auth().useUserAccessGroup("APP_GROUP_NAME")

            print(Auth.auth().currentUser?.uid, "user")
        } catch let error as NSError {
            print("Error changing user access group: %@", error)
        }
    }

RohitRajendran avatar Apr 06 '21 23:04 RohitRajendran

Hi @RohitRajendran, would you mind sharing a minimal repro that I can run locally? It would help us a lot digging into the cause of your issue. Thanks.

rizafran avatar Apr 15 '21 17:04 rizafran

Hey @RohitRajendran. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot avatar Apr 22 '21 01:04 google-oss-bot

Since there haven't been any recent updates here, I am going to close this issue.

@RohitRajendran if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

google-oss-bot avatar Apr 28 '21 01:04 google-oss-bot

Reopening based on conversation in #7840.

Note that watchOS is community supported so it may be awhile before anyone gets to this.

paulb777 avatar Apr 27 '22 23:04 paulb777

Thanks for re-opening, Paul.

It would be good to have a "best practices for watchOS + iOS Firebase apps" doc that lives somewhere, even community-supported and with huge asterisks that it's not official yet.

I think there are a few options:

  1. Pass a Token Have the iPhone perform the auth flow and then retrieve a current token via Auth.auth().currentUser.getIDToken, then pass the token to the watch via a WCSession updateApplicationContext on session activation. Until the watchOS app receives the app context, it will have to display a "please log in on your iPhone" message to the user. This has the advantage of not actually requiring FirebaseAuth on the watchOS client at all - the watch app can just use the token in e.g. server API calls directly. The iOS app will be responsible for keeping its watch companion updated as the token renews and the watchOS app may need to enquire about a new token with the iOS parent app if it hears back that the token has expired.

The disadvantage of this approach is that it does not allow for a standalone watchOS app; the watch can only be used as a companion to the iOS app. In reality, very few apps are - or need to be - standalone; the watchOS App Store is a bit of a joke. So this should be fine for most developers.

  1. iCloud Keychain Sync Use a group/shared keychain between apps and enable iCloud Keychain synchronization, then direct Firebase Auth to use this shared keychain. In theory this enables all of a user's devices that are signed in with their iCloud account to also now be signed in with Firebase as the same user.

I haven't gotten this approach working yet but in theory it could/should work and it enables a full "acts like a peer" approach for watchOS as a client that enables an independent watchOS app experience. The phone and watch don't need to be communicating directly for this to work either; e.g. if the watch has a cellular connection it should be able to complete the iCloud sync.

  1. Proxy Have the iOS app pull all required data for the watchOS app and send it as app State. When the watchOS app opens, have it request information from (and send information to) the iPhone via WCSession messages.

This has the advantage of allowing for a much simpler and smaller watchOS client - the client never makes any "network" requests at all, just chatting with the host iOS app. No Firebase on-watch! The iOS app can use more sophisticated protocols like gRPC to talk to server backends to fulfill the request, none of which the watchOS app needs to be worried about. And the iOS app can just re-use existing code paths already needed to implement these things. The disadvantage is that all operations must be done in actively paired mode; the watchOS app will stop working altogether without an active WCSession, so the watch can never be far from the phone. That's probably an acceptable tradeoff for most apps.

  1. [DOESN'T WORK] App Group

It should be made clear to developers that setting up an app group for their watchOS extension and iOS app and then asking Firebase Auth to use that app group will NOT magically allow Firebase Auth to work correctly on-watch because app groups are used for on-device sharing and the watch and phone are separate devices. Clearly spelling this out will save developers heartache trying to figure out what's wrong.

dweekly avatar Apr 28 '22 16:04 dweekly

@dweekly You are right, app group doesn't support data sharing between watch and iPhone. So unfortunately this issue is expected. We are looking at the possibilities of supporting this feature. In the meantime, @RohitRajendran and @dweekly It would be great to file a feature request and upvote the issue, which helps us prioritize.

charlotteliang avatar Nov 30 '22 19:11 charlotteliang

@RohitRajendran @dweekly Can you share a bit about your use case? Do you use other Firebase products on watch that need authentication or you want to only show user data when authentication is confirmed on watch?

For the second case, you can use WatchConnectivity to notify you watch if user has authenticated on the iPhone.

charlotteliang avatar Dec 14 '22 19:12 charlotteliang

I'm trying to figure this out right now. @dweekly I'm hesitant to go for the iCloud Keychain sync since I suppose it takes some time for it to sync / will be hard to control that. I don't really just want to share the ID token retrieved via User.getIDTokenResult() either as it's short-lived and I want the watch app to be able to act independently from the iPhone. I could provide the long-lived refresh token via WatchConnectivity but there doesn't seem a way to use that to actually sign the user in (set the User object) using the Auth framework through public APIs. I could maybe instead read the whole archived plist data from the keychain, send that to the watch, store it in the keychain there and only then initialize the Auth framework and have it read that data. Doesn't really sound elegant to me though. Has anybody figured this out yet? I'll probably go for providing the refresh token via WatchConnectivity, request the ID token from that (via REST) and use it to request Firebase APIs. That means I can't use any of the frameworks on the Watch though as they need the user object to be set for the Auth framework, right?

qusc avatar Jun 13 '23 14:06 qusc