Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement passwordless authentication in native iOS/Swift applications using MojoAuth OIDC with AppAuth.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-18 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 67% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 50% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 117% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 140% | 0% |
This expert AI assistant guide walks you through integrating passwordless authentication into an existing iOS application using MojoAuth's Hosted Login Page as an OIDC identity provider via AppAuth for iOS. MojoAuth handles all authentication methods (Magic Links, Email OTP, SMS OTP, Social Login, Passkeys) through its hosted page.
openid/AppAuth-iOS.your-app.mojoauth.com).com.example.myapp://auth/callback).> Note: For native/mobile apps, use Authorization Code with PKCE (no Client Secret on the device).
Add AppAuth via Swift Package Manager:
https://github.com/openid/AppAuth-iOS.git.Or via CocoaPods:
ruby# Podfile pod 'AppAuth'
bashpod install
Add a custom URL scheme in your Info.plist:
xml<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>com.example.myapp</string> </array> <key>CFBundleURLName</key> <string>com.example.myapp</string> </dict> </array>
Create an auth configuration helper (e.g., AuthConfig.swift):
swift// AuthConfig.swift import Foundation struct AuthConfig { static let issuerURL = URL(string: "https://your-app.mojoauth.com")! static let clientID = "your_client_id" static let redirectURI = URL(string: "com.example.myapp://auth/callback")! static let scopes = ["openid", "profile", "email"] }
Create a centralised auth manager (e.g., AuthManager.swift):
swift// AuthManager.swift import UIKit import AppAuth class AuthManager: NSObject { static let shared = AuthManager() var currentAuthorizationFlow: OIDExternalUserAgentSession? var authState: OIDAuthState? func login(from viewController: UIViewController, completion: @escaping (Result<OIDAuthState, Error>) -> Void) { // Discover OIDC configuration OIDAuthorizationService.discoverConfiguration(forIssuer: AuthConfig.issuerURL) { config, error in guard let config = config else { completion(.failure(error ?? NSError(domain: "OIDC", code: -1, userInfo: [NSLocalizedDescriptionKey: "Discovery failed"]))) return } // Build authorization request let request = OIDAuthorizationRequest( configuration: config, clientId: AuthConfig.clientID, scopes: AuthConfig.scopes, redirectURL: AuthConfig.redirectURI, responseType: OIDResponseTypeCode, additionalParameters: nil ) // Launch auth flow — opens MojoAuth Hosted Login Page self.currentAuthorizationFlow = OIDAuthState.authState( byPresenting: request, presenting: viewController ) { authState, error in if let authState = authState { self.authState = authState print("Authenticated! Access Token: \(authState.lastTokenResponse?.accessToken ?? "nil")") completion(.success(authState)) } else { print("OIDC Error: \(error?.localizedDescription ?? "Unknown error")") completion(.failure(error ?? NSError(domain: "OIDC", code: -1))) } } } } func logout() { authState = nil } }
swift// AppDelegate.swift (or SceneDelegate.swift) import AppAuth // In AppDelegate: func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { if let flow = AuthManager.shared.currentAuthorizationFlow, flow.resumeExternalUserAgentFlow(with: url) { AuthManager.shared.currentAuthorizationFlow = nil return true } return false } // If using SceneDelegate: func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } if let flow = AuthManager.shared.currentAuthorizationFlow, flow.resumeExternalUserAgentFlow(with: url) { AuthManager.shared.currentAuthorizationFlow = nil } }
Since MojoAuth handles all authentication on its Hosted Login Page, your login screen only needs a "Sign In" button:
swift// LoginViewController.swift import UIKit class LoginViewController: UIViewController { private let signInButton = UIButton(type: .system) override func viewDidLoad() { super.viewDidLoad() setupUI() } private func setupUI() { view.backgroundColor = .systemBackground title = "Welcome" let titleLabel = UILabel() titleLabel.text = "Welcome" titleLabel.font = .systemFont(ofSize: 28, weight: .bold) titleLabel.textAlignment = .center let subtitleLabel = UILabel() subtitleLabel.text = "Sign in with your preferred method" subtitleLabel.font = .systemFont(ofSize: 14) subtitleLabel.textColor = .secondaryLabel subtitleLabel.textAlignment = .center signInButton.setTitle("Sign In with MojoAuth", for: .normal) signInButton.addTarget(self, action: #selector(handleSignIn), for: .touchUpInside) signInButton.titleLabel?.font = .systemFont(ofSize: 17, weight: .semibold) let poweredByLabel = UILabel() poweredByLabel.text = "Powered by MojoAuth" poweredByLabel.font = .systemFont(ofSize: 12) poweredByLabel.textColor = .tertiaryLabel poweredByLabel.textAlignment = .center let stack = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel, signInButton, poweredByLabel]) stack.axis = .vertical stack.spacing = 16 stack.translatesAutoresizingMaskIntoConstraints = false view.addSubview(stack) NSLayoutConstraint.activate([ stack.centerYAnchor.constraint(equalTo: view.centerYAnchor), stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24), stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24), ]) } @objc private func handleSignIn() { AuthManager.shared.login(from: self) { result in DispatchQueue.main.async { switch result { case .success: let dashboard = DashboardViewController() self.navigationController?.pushViewController(dashboard, animated: true) case .failure(let error): let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) self.present(alert, animated: true) } } } } }
OIDAuthState.authState(byPresenting:) call in a coordinator or use ASWebAuthenticationSession directly.Other measured skills in the registry, with their headline benchmark lift.