9

I am making some API Firebase calls (download some user specific data) in my app share extension but the user is not signed into Firebase (aka currentUser is nil) so I don't get any data because I need the uid. In other words, the database only allows authenticated users to access the data. How can I fix this?

Cesare
  • 9,139
  • 16
  • 78
  • 130
  • Were you able to solve this? My current workaround is that when user signs in the containing app then `currentUser.uid` is saved to a shared container (`UserDefaults.init?(suiteName...)`) that can be queried in the extension. I'm still working on how to trigger authentication when the initial sign-in is missing. – toraritte Mar 06 '18 at 12:50
  • @toraritte haven't personally found a solution but you're on the right track. Please check the new answer that someone posted below. If you find a way to make it work please answer this question. – Cesare Mar 07 '18 at 16:19
  • I asked a [more generic question](https://stackoverflow.com/questions/49134868/how-to-officially-handle-unauthenticated-users-in-an-ios-share-extension) dealing with this same issue. If I can get it to work, I'll post it for sure. – toraritte Mar 07 '18 at 17:14

3 Answers3

11

To expand on KutakMir's answer, these steps worked for us:

Step 1. Set up Firebase for the share extension

Steps from this Stackoverflow answer:

  1. Copy the containing app's GoogleService-Info.plist into your extension in Xcode
  2. Drag the copied GoogleService-Info.plist into Xcode into your share extension and
  3. Change the BUNDLE_ID to the name of your share extension's target
  4. Add new target to your Podfile
  5. Install dependencies (pod install)
  6. Configure the Firebase application object in your extension

Step 2. Set up shared container between containing (i.e., main) app and shared extension

Described in App Extension Programming Guide: Handling Common Scenarios. Basically turn on "App Groups" at the Capabilities tab for both your targets (i.e., containing app and share extension), and provide the same designation at both places.

Step 3. Set up keychain sharing

The basic steps are similar to step 2: turn on "Keychain Sharing" at the Capabilities tab for both targets, and provide the designation at both places. (In more detail in the official docs.)

Make sure to append your APP_ID to the Keychain access group when using the queries in the docs! The link above points this out, but this Github issue comment is more useful.

Step 4. Use the Keychain and shared container to synchronize user access

Our workflow:

  • On login (either in share extension or main app), save user ID to the shared container, and save username and password to the Keychain using the user ID as the key

  • On startup (either share extension or main app), check shared container for user ID. If there, it means that user already signed in either the main app or the share extension therefore they should be signed into both. So retrieve credentials from Keychain, and sign in user using signIn(withEmail:password:completion:)

Our project at the point when everything was set up. It is messy with lots of duplication, but works. Relevant files are

  • Access News/LoginViewController.swift
  • Access News/SessionStartViewController.swift
  • Access News/NVC.swift
  • Access-News-Uploader/UploaderNavigationViewController.swift

Tracking down Keychain error codes

This was the most frustrating part. The error codes are of type OSStatus, and they are listed in Security Framework Result Codes, but sometimes one just sees a signed integer when testing. I couldn't find an official list, but OSStatus.com will save a lot of frustration. For example, here's a query of all the Security Framework codes.


Other useful links to official Keychain docs:

toraritte
  • 6,300
  • 3
  • 46
  • 67
  • How would you handle if the user was signing in via Facebook with no username/password? – temp Mar 10 '19 at 17:23
  • I would guess it would be along the same lines? Sorry, haven't worked with Facebook logins, and most of my results came from long and painful experiments... – toraritte Mar 11 '19 at 14:50
2

Go to your main target and extension target and setup keychain sharing. The Firebase Auth is saving the access token to the keychain.

Miroslav Kuťák
  • 1,875
  • 1
  • 18
  • 24
1

The existing answers did not fix it for me, but I did find this official FireBase documentation.

In summary, the fix was using the app groups capability with same identifier in the app and extension.

Then including the code:

do {
  try Auth.auth().useUserAccessGroup("TEAMID.com.example.group1")
} catch let error as NSError {
  print("Error changing user access group: %@", error)
}

In my tests the authenticated user has the same uid in the main app and the share extension.

HTH

Darius
  • 5,180
  • 5
  • 47
  • 62
  • 2
    I came to the same solution as the current answer. Indeed it also works with facebook and google sign in by storing the related tokens in the keychain. However it doesn't work with Sign in with Apple, because this uses a nonce. So using app groups or keychain sharing is the correct solution. – cybergen Sep 22 '20 at 09:31
  • Hi Darius, I am really struggling to authenticate my user in share extension. Where do you call this code above? Could you maybe have a look at my latest questions? I am stuck with errors... – Chris Nov 07 '20 at 13:06
  • @cybergen is it still working for you? I am trying it but I just get Keychain errors... could you have a look at my latest questions? – Chris Nov 07 '20 at 13:06
  • that's the question: https://stackoverflow.com/questions/64705258/swift-signing-user-in-with-access-group-not-working – Chris Nov 07 '20 at 13:07
  • @Chris I'm using a `SLComposeServiceViewController` and I use this code inside the `didSelectPost` callback. Make sure you have an App Group setup in the Signing & Capabilities of your share extension and app in XCode – Darius Nov 10 '20 at 20:02