Orchestration SDKs

Resuming journeys using magic links on iOS

PingOne Advanced Identity Cloud PingAM iOS

To resume a journey in an iOS app, complete the following tasks:

Step 1. Capturing the resume URI

Configure your iOS application to launch when the user clicks the magic link in their email.

After launching, capture the URI that was clicked so you can resume authentication.

Step 2. Resuming a suspended authentication journey

Pass the captured URI into the journey.resume() method to continue the journey, rather than calling journey.start() to start again.

Step 1. Capturing the resume URI

Your application must capture the resume URI that the server emails to the user. That URI contains a unique suspendedId parameter that it uses to resume the user’s journey on the server.

You must register your iOS app to launch when the user clicks the resume URI, so that they can continue their journey in your app. If you don’t register an app to handle the resume URI, clicking a magic link launches your server’s login UI in a browser instead.

We recommend using universal links to associate your iOS app with your resume URI.

Your app should capture the URI value that opened it, as you’ll need to pass that value back to your server to resume the authentication journey.

Since universal links could provide a potential attack vector, you must validate any incoming resumeUri.

A malicious actor might attempt to craft a URL to compromise your app, so ensure you discard any malformed or unexpected URLs and only use the link to resume an authentication journey.

Where you capture the URI depends on how you have set up your app. Commonly, you’ll capture the URI in an AppDelegate or SceneDelegate file:

  • AppDelegate

  • SceneDelegate

Example of capturing the resume URI that opened an iOS app in AppDelegate
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    // Get resume URI from the incoming user activity
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL else {
        return false
    }
}
Example of capturing the resume URI that opened an iOS app in SceneDelegate
func scene(_ scene: UIScene, willConnectTo
           session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {

    // Get resume URI from the incoming user activity
    guard let userActivity = connectionOptions.userActivities.first,
        userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL else {
        return
    }
}

Step 2. Resuming a suspended authentication journey

After obtaining the resumeUri, use it in your application to continue the user’s authentication or registration journey.

Use the Journey module’s resume() method, rather than start(), to continue the journey using the resumeUri as follows:

Resuming a journey on iOS
let node = await journey.resume(resumeUri)

// Handle nodes just like starting new journey...
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 (e.g., 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 (e.g., network issues, unexpected errors like parsing response)
    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
}

If the URI scheme, host, or port in the resumeUri does not match the server configured for the journey, the SDK throws an exception.