iOS Swift - Facebook Login

View on Github

iOS Swift - Facebook Login

Gravatar for rita.zerrizuela@auth0.com
By Rita Zerrizuela

This tutorial demonstrates how to add user login to a Swift application using native Facebook Login. We recommend that you log in to follow this quickstart with examples configured for your account.

I want to explore a sample app

2 minutes

Get a sample configured with your account settings or check it out on Github.

View on Github
System requirements: CocoaPods/Carthage | Xcode 11+ | iOS 13+ Simulator/Device

This tutorial describes how to implement login with the Facebook SDK. ​

Before You Start

  • Install and configure the Facebook Login SDK. You’ll also go through the process of creating a Facebook app in https://developers.facebook.com. When you finish this step, you should have a mobile app running with Facebook Login integrated.
  • Configure your Auth0 application in the dashboard to use Facebook Native Sign In. See Add Facebook Login to Native Apps. When you finish this step, your application will be able to implement Facebook Native Login.

Set up the “Continue with Facebook” button

This guide will help you add authentication with Auth0 to the application you built in the first step.

Request Facebook permissions

Your application is already able to sign in with Facebook. However, to ensure you have a rich user profile, you need to update the permissions with which the Facebook Login Button was set up.

Set the requested permissions to .publicProfile and .email. This way, the user email will also be included as part of the response, provided the access request is accepted by the user.

let loginButton = FBLoginButton(permissions: [.publicProfile, .email])

Now, to kick off the authentication process with Auth0, create a new method in which you will prepare the payload to be sent.

In the sample, the method was named login(with:).

fileprivate func login(with accessToken: FacebookLogin.AccessToken) {
    // TODO
}

Call this method from the Facebook Login Button delegate loginButton(_:didCompleteWith:error:) method, as shown below:

extension ViewController: LoginButtonDelegate {

    func loginButton(_ loginButton: FBLoginButton, didCompleteWith result: LoginManagerLoginResult?, error: Error?) {
        guard error == nil, let accessToken = result?.token else {
            return print(error ?? "Facebook access token is nil")
        }

        login(with: accessToken) // 👈🏻
    }

    func loginButtonDidLogOut(_ loginButton: FBLoginButton) {
        print("Logged out")
    }

}

Integrate Facebook

When you sign in with Facebook at Auth0, the backend will perform some checks in the background to ensure the user is who they say they are. To achieve this, it needs to be provided with a Session Access Token.

Furthermore, if a user needs to be created on Auth0 to represent this Facebook user, the backend will require some of their information, such as their name, last name, and email. The email, if provided, will be flagged as non-verified on the Auth0 user profile.

To obtain the Session Access Token and the user profile, two additional requests need to be made against the Facebook API.

Fetch Facebook session Access Token

Make a new GET request against the Facebook API's /oauth/access_token endpoint. Use the following query parameters:

  • grant_type: fb_attenuate_token.
  • fb_exchange_token: the access token received upon login.
  • client_id: your App ID. This value comes from the Facebook Developer's dashboard and should already be in use in your application if you have integrated Facebook Login successfully.

Put the logic from this step in its own method. You will be calling it later from the previously-added method.

The sample uses URLSession with Combine to perform this request.

private let fbAPIURL = "https://graph.facebook.com/v6.0"

private func fetch(url: URL) -> AnyPublisher<[String: Any], URLError> {
    URLSession.shared.dataTaskPublisher(for: url)
        .subscribe(on: DispatchQueue.global(qos: .userInitiated)) // Execute the request on a background thread
        .receive(on: DispatchQueue.main) // Execute the sink callbacks on the main thread
        .compactMap { try? JSONSerialization.jsonObject(with: $0.data) as? [String: Any] } // Get a JSON dictionary
        .eraseToAnyPublisher()
}

private func fetchSessionAccessToken(appId: String, accessToken: String) -> AnyPublisher<String, URLError> {
    var components = URLComponents(string: "\(fbAPIURL)/oauth/access_token")!
    components.queryItems = [URLQueryItem(name: "grant_type", value: "fb_attenuate_token"),
                             URLQueryItem(name: "fb_exchange_token", value: accessToken),
                             URLQueryItem(name: "client_id", value: appId)]

    return fetch(url: components.url!)
        .compactMap { $0["access_token"] as? String } // Get the Session Access Token
        .eraseToAnyPublisher()
}

Fetch Facebook user profile

Now make another GET request, just like in the step above. The endpoint path will be the User ID value from the Facebook login result (for example, /904636746222815). Use the following parameters:

  • access_token: the access token received upon login.
  • fields: the fields from the user profile that you'd like to get back in the response. These are directly tied to the Facebook Login Button permissions that were configured at the beginning. When a permission is optional, the user must first consent to give access to it. For the purpose of signing up a user at Auth0, their full name and email will suffice.
private func fetchProfile(userId: String, accessToken: String) -> AnyPublisher<[String: Any], URLError> {
    var components = URLComponents(string: "\(fbAPIURL)/\(userId)")!
    components.queryItems = [URLQueryItem(name: "access_token", value: accessToken),
                             URLQueryItem(name: "fields", value: "first_name,last_name,email")]

    return fetch(url: components.url!)
}

Integrate Auth0

Now that the required artifacts have been obtained, you are ready to trade them for Auth0 user credentials, such as the ID and Access Tokens. But first, you must set up the Auth0 SDK to make that last request.

Get your application keys

Go to the Applications section of the Auth0 Dashboard and select the existing application in which you enabled Sign in with Facebook. If you need help with this step, please check the requirements section at the top of this article.

Copy the Domain and Client ID values from the application settings page. These are required by the SDK.

Go ahead and add an Auth0.plist file to your iOS application's main bundle to store them.

<!-- Auth0.plist -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>ClientId</key>
  <string>YOUR_CLIENT_ID</string>
  <key>Domain</key>
  <string>YOUR_DOMAIN</string>
</dict>
</plist>

Install the Auth0 SDK

Cocoapods

If you are using Cocoapods, add this line to your Podfile:

pod 'Auth0', '~> 1.0'

Then run pod install.

For more information on Cocoapods, check their official documentation.

Carthage

If you are using Carthage, add the following line to your Cartfile:

github "auth0/Auth0.swift" ~> 1.0

Then run carthage bootstrap.

For more information about Carthage usage, check their official documentation.

SPM

If you are using the Swift Package Manager, open the following menu item in Xcode:

File > Swift Packages > Add Package Dependency...

In the Choose Package Repository prompt add this url:

https://github.com/auth0/Auth0.swift.git

Then press Next and complete the remaining steps.

For further reference on SPM, check its official documentation.

Web Authentication

If your iOS application plans to support Web Authentication, head over here to learn how to configure the Callback and Logout URLs, and set up the Custom URL Scheme.

Exchange the received data for Auth0 tokens

With the SDK installed, it's time to go back to the first method you added and tie everything together.

Add logic in the method to execute the requests you prepared in the previous steps. Then, pass the resulting Session Access Token and user profile to the SDK method login(facebookSessionAccessToken:profile:). Don't forget to import the SDK first, with import Auth0.

fileprivate func login(with accessToken: FacebookLogin.AccessToken) {
    // Get the request publishers
    let sessionAccessTokenPublisher = fetchSessionAccessToken(appId: accessToken.appID,
                                                              accessToken: accessToken.tokenString)
    let profilePublisher = fetchProfile(userId: accessToken.userID, accessToken: accessToken.tokenString)

    // Start both requests in parallel and wait until all finish
    _ = Publishers
        .Zip(sessionAccessTokenPublisher, profilePublisher)
        .sink(receiveCompletion: { completion in
            if case .failure(let error) = completion {
                print(error)
            }
        }, receiveValue: { sessionAccessToken, profile in
            // Perform the token exchange
            Auth0
                .authentication()
                .login(facebookSessionAccessToken: sessionAccessToken, profile: profile)
                .start { result in
                    switch result {
                    case .success(let credentials): print(credentials) // Auth0 user credentials 🎉
                    case .failure(let error): print(error)
                }
            }
        })
}

To learn more about the Credentials object, check out its source.

Use Auth0 for FREE