ForgeRock Developer Experience

Step 8. Enable authentication

This section describes how to use the abstraction layer to authenticate and how to write the required UI elements to handle nodes and callbacks.

To initiate authentication with FRUser

  1. Use the FRUser class to initiate the authentication tree when the user presses the login button:

    @IBAction func loginButtonPressed(sender: UIButton) {
        print("Login button is pressed")
    
        FRUser.login {(user: FRUser?, Node, Error) in
        }
    }

    In this example, FRUser.login() initiates an authentication tree flow and returns one of the following values:

    FRUser

    If the credentials already exist to authenticate the user session.

    Node

    If the authentication tree requires user input. For example, "Username/Password".

    Error

    If an error occurs during the authentication flow.

    The first step in the authentication tree is to collect the username and password. Therefore, the first object returned here is a Node object that allows the authentication process to continue.

  2. Create a helper method to handle node processing.

    Since the authentication process is iterative and continues until it returns the FRUser object, you need to write a lot of repetitive code. A helper method makes this task easier.

    The following example helper method expects whatever the node returns:

    func handleNode(user: FRUser?, node: Node?, error: Error?) {
    }
  3. You can then pass the returned node (and any subsequent nodes) to this helper method:

    FRUser.login {(user: FRUser?, node, error) in
        self.handleNode(user: user, node: node, error: error)
    }

    When the user presses the login button, the SDK initiates the authentication tree and passes the returned object to the handleNode() method.

  4. The handleNode() method needs to do the following:

    • Complete the process when the user object is returned.

    • Handle any callbacks in the node.

    • Handle any errors.

      These aspects are covered in the following code:

      1. If a user object is returned (the user exists), authenticate the user.

        Note that the main thread must handle any UI changes:

        if let _ = user {
            print("User is authenticated")
        
            DispatchQueue.main.async {
                self.updateStatus()
            }
        }
      2. If a node object is returned, handle the node object:

        else if let node = node {
            print("Node object received, handle the node")
        }
      3. If an error is returned, print the error:

        else {
            print ("Something went wrong: \(String(describing: error))")
        }

    Your complete ViewController.swift file should now resemble the following:

    //
    //  ViewController.swift
    //  DemoApp
    //
    
    import UIKit
    import FRAuth
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var statusLabel: UILabel?
        @IBOutlet weak var loginButton: UIButton?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            FRLog.setLogLevel([.error, .network])
    
            do {
                try FRAuth.start()
                print ("SDK initialized successfully")
            } catch {
                print (error)
            }
    
            updateStatus()
        }
    
        func updateStatus() {
            if let _ = FRUser.currentUser {
                statusLabel?.text = "User is authenticated"
            }
            else {
                statusLabel?.text = "User is not authenticated"
            }
        }
    
        func handleNode(user: FRUser?, node: Node?, error: Error?) {
            if let _ = user {
                print("User is authenticated")
    
                DispatchQueue.main.async {
                    self.updateStatus()
                }
            }
            else if let node = node {
                print("Node object received, handle the node")
            }
            else {
                print ("Something went wrong: \(String(describing: error))")
            }
        }
    
        @IBAction func loginButtonPressed(sender: UIButton) {
            print("Login button is pressed")
    
            FRUser.login {(user: FRUser?, node, error) in
                self.handleNode(user: user, node: node, error: error)
            }
        }
    }
  5. Save changes to the ViewController.swift file.

  6. Run the application, and click the Login button.

    The application output shows the node object in the response data that AM returns.

    The handleNode() function now needs to handle the nameCallback callback and the passwordCallback.