---
title: Navigating an authentication journey on iOS
description: PingOne Advanced Identity Cloud PingAM iOS
component: orchsdks
page_id: orchsdks:journey:usage/ios/05-navigating-an-authentication-journey
canonical_url: https://developer.pingidentity.com/orchsdks/journey/usage/ios/05-navigating-an-authentication-journey.html
revdate: Tue, 2 Dec 2025 16:32:08 +0000
section_ids:
  handling_continuenode: Handling ContinueNode
  handling_failurenode_and_errornode: Handling FailureNode and ErrorNode
  handling_successnode: Handling SuccessNode
---

# Navigating an authentication journey on iOS

[icon: circle-check, set=far]PingOne Advanced Identity Cloud [icon: circle-check, set=far]PingAM [icon: apple, set=fab]iOS

* [Prepare](01-configuring-the-server.html)

* [Install](02-installing-the-journey-module.html)

* [Configure](03-configuring-the-journey-module.html)

* [Start](04-starting-an-authentication-journey.html)

* **Navigate**

* [Manage](06-handling-sessions.html)

The `start()` method returns a `Node` instance, which represents where you are in the authentication journey.

There are four main types of `Node` instance a journey can return.

**The four different types of node instances**

| Node type      | Description                                                                                                                                                           |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ContinueNode` | Indicates a step in the middle of the authentication journey.Call `node.next()` to submit any collected data and advance to the next node.                            |
| `ErrorNode`    | Represents a bad request, such as invalid credentials. For example a bad password or incorrect one-time passcode (OTP).Access the error message using `node.message`. |
| `FailureNode`  | Signifies an unexpected error during the authentication process, such as network connectivity issues.Retrieve the underlying cause using `node.cause`.                |
| `SuccessNode`  | Indicates successful authentication.Obtain the user session details using `node.session`.                                                                             |

Handle the different node types as follows:

Handling the different node types in a journey

```swift
let node = await journey.start("sdkUsernamePasswordJourney")

// Determine the type of the current Node
switch node {
    case let continueNode as ContinueNode:
        // Proceed to the next step in the authentication journey
        let nextNode = continueNode.next()

    case let errorNode as ErrorNode:
        // Handle server-side errors, such as invalid credentials
        let errorMessage = errorNode.message
        let input = errorNode.input // Access the raw JSON response with the input attribute
        // Display error to the user

    case let failureNode as FailureNode:
        // // Handle unexpected errors, such as network issues, parsing response problems
        let errorCause = failureNode.cause
        // Log the error and potentially display a generic message

    case let successNode as SuccessNode:
        // Authentication successful!
        // Retrieve the session
        let session = successNode.session
        let input = successNode.input // Access the raw JSON response with the input attribute
        // Proceed with post-authentication actions

    default:
        break
}
```

## Handling ContinueNode

The `ContinueNode` type often contains a list of callbacks that require input from the client.

You can access these using `node.callbacks()`, and provide the necessary information to each relevant callback. Ensure you call `next()` to submit the collected data and traverse through the journey nodes.

Handling different callbacks

```swift
if let current = node as? ContinueNode {
      current.callbacks.forEach { callback in
          switch callback {
           case let nameCallback as NameCallback:
              nameCallback.name = "Your Username"
          case let passwordCallback as PasswordCallback:
              passwordCallback.password = "Your Password"
              // Handle other callback types as they are introduced
          default:
              break
          }
     }
}

// Proceed to the next Node with the provided input
let nextNode = current.next()
```

Specific callback types provide their own properties for accessing labels and setting values:

Accessing callback prompts and setting input data

* `NameCallback`

* `PasswordCallback`

```swift
let prompt = (callback as? NameCallback)?.prompt // Access the prompt/label
(callback as? NameCallback)?.name = "Your Username" // Set the user's input
```

```swift
let prompt = (callback as? PasswordCallback)?.prompt // Access the prompt/label
(callback as? PasswordCallback)?.password = "Your Password" // Set the user's password
```

|   |                                                                                                                        |
| - | ---------------------------------------------------------------------------------------------------------------------- |
|   | Learn about supported callbacks in [Supported Nodes and Callbacks](../../compatibility.html#supported-journey-fields). |

## Handling FailureNode and ErrorNode

The **Journey** module distinguishes between the `FailureNode` and `ErrorNode` types for different categories of errors encountered during the authentication flow.

* `FailureNode`

  Indicates an unexpected issue that prevents the journey from continuing. This could stem from network problems or data parsing errors.

  You can access the underlying `Throwable` that caused the error using `node.cause()` 1.

  You should display a user-friendly generic error message and log the details for support investigation.

* `ErrorNode`

  Signifies an error response from the authentication server, typically an HTTP `4xx` or `5xx` status code.

  These errors often relate to invalid user input or issues server-side.

  You can retrieve the specific error message provided by the server using `node.message()` 2, and access the raw JSON response by using `node.input` 3.

Handling FailureNodes and ErrorNodes

```swift
let node = await journey.start("sdkUsernamePasswordJourney")

switch node {
    case let continueNode as ContinueNode:
        // ...
        break

    case let failureNode as FailureNode:
        // Retrieve the underlying error
        let errorCause = failureNode.cause (1)
        // Log the errorCause for debugging
        // Display a generic error message to the user

    case let errorNode as ErrorNode:
        // Retrieve the server-provided error message
        let errorMessage = errorNode.message (2)
        // Access the raw JSON error response
        let rawResponse = errorNode.input (3)
        // Display the specific error message to the user

    case let successNode as SuccessNode:
        // ...
        break

    default:
        break
}
```

## Handling SuccessNode

WHen the **Journey** module reaches the `SuccessNode` type it securely stores the session token. Use the `journeyUser()` function to create an object representing the user, and call `token()1` on that object to obtain the session token itself:

Handling SuccessNode

```swift
let node = await journey.start("sdkUsernamePasswordJourney")

switch node {
    case let continueNode as ContinueNode:
        let token: Result<Token, OidcError>?
        // Checking the user object
        let journeyUser = await journey.journeyUser()

        // Retrieve the session token, if available
        token = journeyUser.token()
        break

    case let failureNode as FailureNode:
        // ...
        break

    case let errorNode as ErrorNode:
        // ...
        break

    case let successNode as SuccessNode:
        // ...
        break

    default:
        break
}
```

If you integrated the **OIDC** module, it provides methods to interact with OpenID Connect tokens, such as obtaining data from the user info endpoint, or revoking the access token:

Managing OpenID Connect tokens with the OIDC module integrated

```swift
let node = await journey.start("sdkUsernamePasswordJourney")

switch node {
    case let continueNode as ContinueNode:
        let token: Result<Token, OidcError>?
        // Checking the user object
        let journeyUser = await journey.journeyUser()

        // Retrieve the session token, if available
        token = journeyUser.token()

        switch token {
            case .success(let token):
                await MainActor.run {
                    self.token = String(describing: token)
                    let accessToken = token.accessToken
                }
                LogManager.standard.i("AccessToken: \(self.token.accessToken)")
            case .failure(let error):
                await MainActor.run {
                    self.token = "Error: \(error.localizedDescription)"
                }
                LogManager.standard.e("", error: error)
            case .none:
                break
            }
        }

        // Fetch user information using the access token (if valid)
        journeyUser.userinfo()

        // Revoke the current access and refresh tokens
        journeyUser.revoke()

        // Initiate the logout process, potentially clearing local session data
        journeyUser.logout()
        break

    case let failureNode as FailureNode:
        // ...
        break

    case let errorNode as ErrorNode:
        // ...
        break

    case let successNode as SuccessNode:
        // ...
        break

    default:
        break
}
```
