Skip to main content

Overview

Para One Click Login is the default experience—Para hosts the entire flow for existing users and new signups. If you turn on passkey or password requirements in the Developer Portal, the optional sections below show how to handle them.
Beta Testing Credentials In the BETA Environment, you can use any email ending in @test.getpara.com (like dev@test.getpara.com) or US phone numbers (+1) in the format (area code)-555-xxxx (like (425)-555-1234). Any OTP code will work for verification with these test credentials. These credentials are for beta testing only. You can delete test users anytime in the beta developer console to free up user slots.

Prerequisites

To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.
Don’t have an API key yet? Request access to the Developer Portal to create API keys, manage billing, teams, and more.
Need to install the SDK? Start with the Swift Setup Guide.
Para ships with One Click enabled. You can add requirements like Passkey-only signup or password fallback from the Developer Portal. Adjust the optional sections below only if you turn on those features.

Start the Authentication Flow

Inject the Para manager and authentication helpers, then launch the flow with whichever identifier your UI collects (email or phone). Para One Click handles the rest unless you enable extra verification.
AuthenticationView.swift
import AuthenticationServices
import ParaSwift

struct AuthenticationView: View {
    @EnvironmentObject var paraManager: ParaManager
    @Environment(\.authorizationController) private var authorizationController
    @Environment(\.webAuthenticationSession) private var webAuthenticationSession

    @State private var pendingState: AuthState? // Only used if you enable OTP/passkey requirements

    var body: some View {
        // Render your auth UI and call startAuth(identifier:)
    }

    private func startAuth(identifier: String) {
        let auth: Auth = identifier.contains("@")
            ? .email(identifier.lowercased())
            : .phone(identifier) // Expect E.164 format

        Task {
            do {
                let state = try await paraManager.initiateAuthFlow(auth: auth)

                if let loginUrl = state.loginUrl {
                    _ = try await paraManager.presentAuthUrl(
                        loginUrl,
                        context: "One Click Login",
                        webAuthenticationSession: webAuthenticationSession
                    )

                    if state.stage == .login {
                        _ = try await paraManager.waitForLogin()
                    } else {
                        _ = try await paraManager.waitForSignup()
                    }

                    completeLogin()
                    return
                }

                switch state.stage {
                case .verify:
                    // Store state to finish OTP/passkey signup later if you enabled it
                    pendingState = state
                case .login:
                    _ = try await paraManager.waitForLogin()
                    completeLogin()
                case .signup:
                    // Signup completes after OTP + passkey if required
                    break
                }
            } catch {
                // Handle errors according to your UI needs
            }
        }
    }
}
The completeLogin() helper can mark your app as authenticated and transition to the signed-in experience:
AuthenticationView.swift
private func completeLogin() {
    // Mark the user as authenticated in your app
}

Handle OTP Signup (Optional)

Only implement this step if you turn on OTP / passkey signup in the Developer Portal. Collect the OTP, exchange it for a verified state, and finish signup:
AuthenticationView.swift
private func submitOtp(_ code: String) {
    guard let pendingState else { return }

    Task {
        do {
            let verifiedState = try await paraManager.handleVerificationCode(
                verificationCode: code
            )

            try await paraManager.handleSignup(
                authState: verifiedState,
                method: .passkey,
                authorizationController: authorizationController,
                webAuthenticationSession: webAuthenticationSession
            )

            completeLogin()
        } catch {
            // Handle errors according to your UI needs
        }
    }
}

Support Direct Passkey Login

If you already know a user has passkeys registered, you can offer a shortcut that skips email/phone input:
AuthenticationView.swift
private func loginWithPasskey(email: String?) {
    Task {
        do {
            try await paraManager.loginWithPasskey(
                authorizationController: authorizationController,
                email: email,
                phone: nil
            )
            completeLogin()
        } catch {
            // Handle errors according to your UI needs
        }
    }
}

Next Steps