iOS - Authentication Module
This module may be subject to additional enhancements and changes in future releases as we continue to evolve its capabilities.
An AuthenticationModule replaces the standard Q2 login screen with a custom one — useful
for FIs or partners that need a bespoke sign-in experience while still handing off to Core
for session setup.
AuthenticationModuleContext now exposes passwordAuthentication, a Core-provided
implementation of standard username/password login. Modules that only need
credential-based authentication no longer have to construct WebServiceRequest calls or
parse response headers themselves.
Quick Start
import Q2ModuleInterfaces
public class CustomAuthModule: Q2ModuleBase {
public override func viewControllerForPlatformAuthentication(
with context: AuthenticationModuleContext
) async throws -> UIViewController? {
// passwordAuthentication is always provided by Core.
let passwordAuth = context.passwordAuthentication
return MyLoginViewController(passwordAuth: passwordAuth, delegate: moduleDelegate)
}
}
func login(username: String, password: String) async {
let credential = PasswordCredential(username: username, password: password)
do {
let result = try await passwordAuth.authenticate(using: credential)
let response = result.asAuthenticationModuleResponse()
try await moduleDelegate.completePlatformAuthentication(with: response)
} catch let error as MobileCoreLoginFailure {
handle(error)
}
}
API Reference
PasswordCredential
public struct PasswordCredential: Sendable {
public let username: String
public let password: String
public init(username: String, password: String)
}
Never log or serialize a PasswordCredential. Use SecureField in SwiftUI, or
isSecureTextEntry = true in UIKit, to collect password input.
PasswordAuthentication
public protocol PasswordAuthentication: AnyObject, Sendable {
func authenticate(using credential: PasswordCredential) async throws -> PasswordAuthenticationResult
}
Throws MobileCoreLoginFailure:
.invalidCredentials(error:statusCode:hydraCode:)— wrong username/password.networkUnavailable(error:)— no connectivity, or the request timed out.genericError(error:statusCode:hydraCode:)— server error or unexpected failure.insecure(reason:)— the security module blocked the login
PasswordAuthenticationResult
public struct PasswordAuthenticationResult: Sendable {
public let statusCode: Int
public let q2token: String?
public let userDataJSON: [String: JSONValue]?
public let username: String?
public let authenticationMethod: AuthenticationMethod
public func asAuthenticationModuleResponse() -> AuthenticationModuleResponse
}
Call asAuthenticationModuleResponse() to convert directly to the type expected by
ModuleDelegate.completePlatformAuthentication(with:).
AuthenticationModuleContext
public struct AuthenticationModuleContext: Sendable {
public var presentWithLocalizedErrorMessage: String?
public var shouldAllowAutoLogin: @MainActor () -> Bool
public var passwordAuthentication: any PasswordAuthentication
}
presentWithLocalizedErrorMessageis set by Core when the user is routed to login from a deep link while logged out (for example, "Please sign in to continue"). Display it as a banner on your login screen.shouldAllowAutoLogin()must be checked before attempting biometric or saved-credential auto-login — it returnsfalsewhen the app was opened from a push notification, so the user sees the notification content instead of being auto-logged in.passwordAuthenticationis always provided by Core; your module decides whether to use it.
Migration from manual HTTP requests
// Before: constructing the request by hand
var request = URLRequest(url: URL(string: "\(baseURL)/mobilews/logonUser")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: [
"userId": username, "password": password,
])
let (data, response) = try await URLSession.shared.data(for: request)
// ...parse status code, q2token header, and response body by hand
// After: PasswordAuthentication
let credential = PasswordCredential(username: username, password: password)
let result = try await passwordAuth.authenticate(using: credential)
let response = result.asAuthenticationModuleResponse()
try await moduleDelegate.completePlatformAuthentication(with: response)
PasswordAuthentication does not cover biometric or SSO/external-identity-provider flows —
continue using the existing AuthenticationModule methods and executeWebServiceRequest for
those.
Related Documentation
- Native Login Module — Q2's own fully native login experience, if you don't need a fully custom implementation
- InboundSSO Module — delegating authentication to an external identity provider instead