Facebook + Google OAuth Issues?

Hi all,

I’ve recently been having issues similar to the ones found in this thread, except I’m using iOS 14/Xcode 12 and Realm Beta 10.0.0-beta.5.

Specifically, both the FB and Google OAuth process lead to this error code:

‘error exchanging access code with OAuth2 provider’, code: 47}

I double-checked that I was using the right Client ID/Secret as well as the proper Redirect URI, and all seems ok on the backend for both Facebook and Google. Now I’ve tried using the updated serverAuthCode method found in the docs here, but the Credentials shoot me an error:

let credentials = Credentials.google(serverAuthCode: "<token>")

Error: Error: Type ‘Credentials’ (aka ‘RLMCredentials’) has no member ‘google’

Is there an update I’m missing or am I missing something in the Credentials setup? Thanks!

Hey Aabesh - can you upgrade to beta.6 and see if this issue persists? This syntax changed in the newest version.

Note: Google OAuth Credentials takes the Auth Code as a parameter and Facebook OAuth credentials take the access token as a parameter, as both orgs provide different OAuth implementation recommendations.

Hey there, thanks for the reply - unfortunately still seeing the same issue with RLMCredentials having no member ‘google’ and not seeing any instance of serverAuthCode working successfully. Here’s what the pod looks like, and I confirmed that the target was correct and all dependencies were updated to 10.0.0.6:

pod ‘RealmSwift’, ‘=10.0.0-beta.6’

Let me know if there’s any specifics I can provide as well.

UPDATE: So the syntax is showing successfully as googleAuthCode instead of googleToken, but I’m still getting Error Code = 47 “error exchanging access code with OAuth2 provider”. Here’s a sample of what I’m doing, note that I can’t do Credentials.google as that’s not being recognized:

let credentials = Credentials.init(googleAuthCode: "<token>")
app.login(credentials: credentials) { (user, error) in
DispatchQueue.main.sync {
...
}

I’ve also made sure to setup a Web OAuth Client ID and an iOS OAuth Client ID, and I’m using the Web application creds in the Realm configuration.

Hey Aabesh,

Ah yeah - I realized there might have been an additional typo for the first error. For the second, I need a bit more information on how you’ve set up your Google Sign in. You do need a Web Oauth Client ID and a iOS OAuth Client Id.

Then you need to take the following steps:

  1. Configure Google OAuth on the cloud with the Web Client ID and Web Client Secret

  1. Add the GoogleSignIn SDK to your iOS project and add your OAuth iOS Keychain to your Xcode project

  2. set the serverClientId (Web Client Id) and the clientID(iOS client Id) properties on your GID.SharedInstance() object (legacy docs show a small snippet for iOS here)

  3. Use user.serverAuthCode on Google login to create your Realm credential


full code example from AppDelegate.swift that I wrote (I’m using pod 'GoogleSignIn', '= 4.4.0', pod 'RealmSwift', '=10.0.0-beta.5')

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // google iOS client ID
    GIDSignIn.sharedInstance()?.clientID = Constants.GOOGLE_CLIENT_ID
    // google web client ID
    GIDSignIn.sharedInstance()?.serverClientID = Constants.GOOGLE_SERVER_CLIENT_ID
    GIDSignIn.sharedInstance()?.delegate = self

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()
    window?.rootViewController = UINavigationController(rootViewController: WelcomeViewController())
    return true
}

// added for google sign-in
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return GIDSignIn.sharedInstance().handle(url as URL?,
                                             sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
                                             annotation: options[UIApplication.OpenURLOptionsKey.annotation])
}

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
    if let error = error {
        print("error received when logging in with Google: \(error.localizedDescription)")
    } else {
        switch user.serverAuthCode {
        case .some:
     
            // Fetch Google token via the Google SDK
            app.login(credentials: Credentials(googleToken: user.serverAuthCode)) { (user, error) in
               DispatchQueue.main.sync {
                   guard error == nil else {
                       print("Login failed: \(error!)")
                       return
                   }
                   // Now logged in, do something with user
               }
            }

        case .none:
            print("serverAuthCode not retreived")
            GIDSignIn.sharedInstance()?.signOut()
        }
        
    }
}
2 Likes

Ah I think my mistake was missing the GoogleSignIn SDK entirely and not setting the serverClientID/Client ID correctly. Thanks for the detailed walkthrough! Was I missing this somewhere in the MongoDB iOS/Google OAuth docs? Might just have totally spaced there but I’m not sure where the steps for the GoogleSignIn SDK were referenced?

I’ve got most of it setup, and now I just need to call the last function for signing in programmatically through a button action. Any tips on how to reference the last function and call the arguments correctly?

I tried this but it’s a no-go given I’m just referencing the GID types and not the actual arguments themselves:

Button(action: {
self.GoogleSignIn(GIDSignIn?, didSignInFor: GIDGoogleUser?, withError: Error?)
})
1 Like

No, they’re not in our docs yet but we’re working on documenting these steps in the near future.

I tried this but it’s a no-go given I’m just referencing the GID types and not the actual arguments themselves:

As for the way I did it, I made sure my AppDelegate conformed to GIDSignInDelegate
and logged into Realm in the sign method.

I would highly suggest you also check other tutorials/examples as my example may not be best practice.

For my button implementation, the GoogleSignIn Button lives in the first ViewController that is shown first and that ViewController conforms to GIDSignInUIDelegate. I believe once you set the delegate to self, you can also set a notification + handler that will push a new view controller on login.


View Controller + Button Snippet

class WelcomeViewController: UIViewController, GIDSignInUIDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Welcome"

        // Do any additional setup after loading the view.
        GIDSignIn.sharedInstance()?.uiDelegate = self
          
         // this notification is called when signIn fails or succeeds
        // NotificationCenter.default.addObserver(self, selector: #selector(signInAndPushVC), name: NSNotification.Name("OAUTH_SIGN_IN"), object: nil)

        
        if //user is logged in {
            self.navigationController?.pushViewController(TodoTableViewController(), animated: true)
        } else {
            
            let googleButton = GIDSignInButton(frame: CGRect(x: self.view.frame.width / 2 - 150, y: 200, width: 300, height: 50))
           
            self.view.addSubview(googleButton)
            
        }
    }
1 Like

Thanks so much @Sumedha_Mehta1! Appreciate the guidance. Looks like GIDSignIn can also be directly called in SwiftUI with GIDSignIn.sharedInstance().signIn().

I found some good examples with SwiftUI in this Medium article, and for those interested, Google has examples on reference classes and the GIDSignIn button here.

2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.