Skip to content

Commit

Permalink
NEVISACCESSAPP-5981: Added Password authenticator capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-gulyas committed Jul 5, 2024
1 parent d657965 commit 59d2dd2
Show file tree
Hide file tree
Showing 38 changed files with 1,387 additions and 613 deletions.
114 changes: 71 additions & 43 deletions NevisExampleApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
6 changes: 6 additions & 0 deletions NevisExampleApp/Configuration/Model/AppConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ struct AppConfiguration: Codable {
/// The Nevis Mobile Authentication SDK configuration.
let sdkConfiguration: Configuration

/// The allowed authenticators.
let authenticatorWhitelist: [AuthenticatorAaid]

// MARK: - CodingKey

/// Enumeration for keys used during coding.
Expand All @@ -25,6 +28,8 @@ struct AppConfiguration: Codable {
case loginConfiguration = "login"
/// Key for the SDK configuration.
case sdkConfiguration = "sdk"
/// Key for the authenticator whitelist.
case authenticatorWhitelist

/// Enumeration for the nested SDK configuration keys used during coding.
enum NestedCodingKeys: String, CodingKey {
Expand Down Expand Up @@ -73,5 +78,6 @@ struct AppConfiguration: Codable {
else {
self.sdkConfiguration = try container.decode(Configuration.self, forKey: .sdkConfiguration)
}
self.authenticatorWhitelist = try container.decode([AuthenticatorAaid].self, forKey: .authenticatorWhitelist)
}
}
4 changes: 2 additions & 2 deletions NevisExampleApp/Coordinator/App/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ protocol AppCoordinator: Coordinator {
/// - Parameter parameter: The navigation parameter.
func navigateToAuthenticatorSelection(with parameter: SelectAuthenticatorParameter)

/// Navigates to the Pin screen.
/// Navigates to the Credential screen.
///
/// - Parameter parameter: The navigation parameter.
func navigateToPin(with parameter: PinParameter)
func navigateToCredential(with parameter: CredentialParameter)

/// Navigates to the Transaction Confirmation screen.
///
Expand Down
24 changes: 12 additions & 12 deletions NevisExampleApp/Coordinator/App/AppCoordinatorImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,26 +138,26 @@ extension AppCoordinatorImpl: AppCoordinator {
rootNavigationController?.pushViewController(screen, animated: true)
}

func navigateToPin(with parameter: PinParameter) {
guard let screen = DependencyProvider.shared.container.resolve(PinScreen.self,
func navigateToCredential(with parameter: CredentialParameter) {
guard let screen = DependencyProvider.shared.container.resolve(CredentialScreen.self,
argument: parameter as NavigationParameterizable) else {
return
}

if let pinScreen = topScreen as? PinScreen {
// the `PinEnrollerImpl` or `PinUserVerifierImpl` navigates to the PIN screen although that is the visible screen.
// It means that the user entered an invalid PIN and a recoverable error recevied.
if let credentialScreen = topScreen as? CredentialScreen {
// the `PinEnrollerImpl`, `PinUserVerifierImpl`, `PasswordEnrollerImpl` or `PasswordUserVerifierImpl` navigates to the Credential screen although that is the visible screen.
// It means that the user entered invalid credentials and a recoverable error recevied.
// Just refresh the screen with the new presenter.
pinScreen.presenter = DependencyProvider.shared.container.resolve(PinPresenter.self,
argument: parameter as NavigationParameterizable)
pinScreen.presenter.view = pinScreen
logger.log("Refreshing Pin screen.", color: .purple)
pinScreen.refresh()
pinScreen.enableInteraction()
credentialScreen.presenter = DependencyProvider.shared.container.resolve(CredentialPresenter.self,
argument: parameter as NavigationParameterizable)
credentialScreen.presenter.view = credentialScreen
logger.log("Refreshing credential (PIN, Password) screen.", color: .purple)
credentialScreen.refresh()
credentialScreen.enableInteraction()
return
}

logger.log("Navigating to Pin screen.", color: .purple)
logger.log("Navigating to Credential screen.", color: .purple)
rootNavigationController?.pushViewController(screen, animated: true)
}

Expand Down
51 changes: 41 additions & 10 deletions NevisExampleApp/Dependency Provider/AppAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ extension AppAssembly: Assembly {

private extension AppAssembly {

// MARK: Screens

/// Registers the screens.
///
/// - Parameter container: The container provided by the `Assembler`.
Expand Down Expand Up @@ -66,8 +68,8 @@ private extension AppAssembly {
argument: arg))
}.inObjectScope(.weak)

container.register(PinScreen.self) { (res: Resolver, arg: NavigationParameterizable) in
PinScreen(presenter: res ~> (PinPresenter.self, argument: arg))
container.register(CredentialScreen.self) { (res: Resolver, arg: NavigationParameterizable) in
CredentialScreen(presenter: res ~> (CredentialPresenter.self, argument: arg))
}.inObjectScope(.weak)

container.register(TransactionConfirmationScreen.self) { (res: Resolver, arg: NavigationParameterizable) in
Expand All @@ -90,6 +92,8 @@ private extension AppAssembly {
.inObjectScope(.container)
}

// MARK: Coordinators

/// Registers the coordinators.
///
/// - Parameter container: The container provided by the `Assembler`.
Expand All @@ -99,6 +103,8 @@ private extension AppAssembly {
.inObjectScope(.container)
}

// MARK: Presenters

/// Registers the presenters.
///
/// - Parameter container: The container provided by the `Assembler`.
Expand Down Expand Up @@ -126,6 +132,7 @@ private extension AppAssembly {
return AuthCloudApiRegistrationPresenter(clientProvider: res~>,
authenticatorSelector: authenticatorSelector,
pinEnroller: res~>,
passwordEnroller: res~>,
biometricUserVerifier: res~>,
devicePasscodeUserVerifier: res~>,
appCoordinator: res~>,
Expand All @@ -141,6 +148,7 @@ private extension AppAssembly {
clientProvider: res~>,
authenticatorSelector: authenticatorSelector,
pinEnroller: res~>,
passwordEnroller: res~>,
biometricUserVerifier: res~>,
devicePasscodeUserVerifier: res~>,
appCoordinator: res~>,
Expand All @@ -155,6 +163,8 @@ private extension AppAssembly {
authenticatorSelector: authenticatorSelector,
pinChanger: res~>,
pinUserVerifier: res~>,
passwordChanger: res~>,
passwordUserVerifier: res~>,
biometricUserVerifier: res~>,
devicePasscodeUserVerifier: res~>,
appCoordinator: res~>,
Expand All @@ -168,9 +178,9 @@ private extension AppAssembly {
initializer: SelectAuthenticatorPresenter.init)
.inObjectScope(.transient)

container.autoregister(PinPresenter.self,
container.autoregister(CredentialPresenter.self,
argument: NavigationParameterizable.self,
initializer: PinPresenter.init)
initializer: CredentialPresenter.init)
.inObjectScope(.transient)

container.autoregister(TransactionConfirmationPresenter.self,
Expand All @@ -193,6 +203,8 @@ private extension AppAssembly {
.inObjectScope(.transient)
}

// MARK: Components

/// Registers the components.
///
/// - Parameter container: The container provided by the `Assembler`.
Expand All @@ -208,13 +220,21 @@ private extension AppAssembly {
container.autoregister(AccountSelector.self,
initializer: AccountSelectorImpl.init)

container.autoregister(AuthenticatorSelector.self,
name: AuthenticationAuthenticatorSelectorName,
initializer: AuthenticationAuthenticatorSelectorImpl.init)
container.register(AuthenticatorSelector.self,
name: RegistrationAuthenticatorSelectorName) { res in
AuthenticatorSelectorImpl(appCoordinator: res~>,
logger: res~>,
configurationLoader: res~>,
operation: .registration)
}

container.autoregister(AuthenticatorSelector.self,
name: RegistrationAuthenticatorSelectorName,
initializer: RegistrationAuthenticatorSelectorImpl.init)
container.register(AuthenticatorSelector.self,
name: AuthenticationAuthenticatorSelectorName) { res in
AuthenticatorSelectorImpl(appCoordinator: res~>,
logger: res~>,
configurationLoader: res~>,
operation: .authentication)
}

container.autoregister(PinEnroller.self,
initializer: PinEnrollerImpl.init)
Expand All @@ -225,6 +245,15 @@ private extension AppAssembly {
container.autoregister(PinUserVerifier.self,
initializer: PinUserVerifierImpl.init)

container.autoregister(PasswordEnroller.self,
initializer: PasswordEnrollerImpl.init)

container.autoregister(PasswordChanger.self,
initializer: PasswordChangerImpl.init)

container.autoregister(PasswordUserVerifier.self,
initializer: PasswordUserVerifierImpl.init)

container.autoregister(BiometricUserVerifier.self,
initializer: BiometricUserVerifierImpl.init)

Expand All @@ -249,6 +278,8 @@ private extension AppAssembly {
authenticationAuthenticatorSelector: authSelectorForAuth,
pinEnroller: res~>,
pinUserVerifier: res~>,
passwordEnroller: res~>,
passwordUserVerifier: res~>,
biometricUserVerifier: res~>,
devicePasscodeUserVerifier: res~>,
appCoordinator: res~>,
Expand Down
4 changes: 4 additions & 0 deletions NevisExampleApp/Error/AppError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ enum AppError: Error {
case authenticatorNotFound
/// No PIN authenticator found in the list of registered authenticators.
case pinAuthenticatorNotFound
/// No Password authenticator found in the list of registered authenticators.
case passwordAuthenticatorNotFound
/// No device information found.
case deviceInformationNotFound
/// No data found during Auth Cloud registration.
Expand Down Expand Up @@ -49,6 +51,8 @@ extension AppError: LocalizedError {
L10n.Error.App.authenticatorNotFound
case .pinAuthenticatorNotFound:
L10n.Error.App.pinAuthenticatorNotFound
case .passwordAuthenticatorNotFound:
L10n.Error.App.passwordAuthenticatorNotFound
case .deviceInformationNotFound:
L10n.Error.App.deviceInformationNotFound
case .authCloudApiRegistrationDataNotFound:
Expand Down

This file was deleted.

92 changes: 92 additions & 0 deletions NevisExampleApp/Interaction/AuthenticatorSelectorImpl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Nevis Mobile Authentication SDK Example App
//
// Copyright © 2022. Nevis Security AG. All rights reserved.
//

import NevisMobileAuthentication

/// The unique name of authenticator selector implementation for Registration operation.
let RegistrationAuthenticatorSelectorName = "auth_selector_reg"

/// The unique name of authenticator selector implementation for Authentication operation.
let AuthenticationAuthenticatorSelectorName = "auth_selector_auth"

/// Default implementation of ``AuthenticatorSelector`` protocol used for authentication.
///
/// Navigates to the ``SelectAuthenticatorScreen`` where the user can select from the available authenticators.
class AuthenticatorSelectorImpl {

enum Operation {
case registration
case authentication
}

// MARK: - Properties

/// The application coordinator.
private let appCoordinator: AppCoordinator

/// The logger.
private let logger: SDKLogger

/// The configuration loader.
private let configurationLoader: ConfigurationLoader

/// The mode of the current operation.
private let operation: Operation

// MARK: - Initialization

/// Creates a new instance.
///
/// - Parameters:
/// - appCoordinator: The application coordinator.
/// - logger: The logger.
/// - configurationLoader: The configuration loader.
init(appCoordinator: AppCoordinator,
logger: SDKLogger,
configurationLoader: ConfigurationLoader,
operation: Operation) {
self.appCoordinator = appCoordinator
self.logger = logger
self.configurationLoader = configurationLoader
self.operation = operation
}
}

// MARK: - AuthenticatorSelector

extension AuthenticatorSelectorImpl: AuthenticatorSelector {
func selectAuthenticator(context: AuthenticatorSelectionContext, handler: AuthenticatorSelectionHandler) {
guard let configuration = try? configurationLoader.load() else {
logger.log("Configuration cannot be loaded during authenticator selection!", color: .red)
return handler.cancel()
}

logger.log("Please select one of the received available authenticators!")

let validator = AuthenticatorValidator()
let validationResult = switch operation {
case .registration: validator.validateForRegistration(context: context, whitelistedAuthenticators: configuration.authenticatorWhitelist)
case .authentication: validator.validateForAuthentication(context: context, whitelistedAuthenticators: configuration.authenticatorWhitelist)
}

switch validationResult {
case let .success(validatedAuthenticators):
let authenticatorItems = validatedAuthenticators.map {
AuthenticatorItem(authenticator: $0,
isPolicyCompliant: context.isPolicyCompliant(authenticatorAaid: $0.aaid),
isUserEnrolled: $0.isEnrolled(username: context.account.username))
}
let parameter: SelectAuthenticatorParameter = .select(authenticatorItems: authenticatorItems,
handler: handler)
if case .registration = operation {
appCoordinator.topScreen?.enableInteraction()
}
appCoordinator.navigateToAuthenticatorSelection(with: parameter)
case .failure:
handler.cancel()
}
}
}
Loading

0 comments on commit 59d2dd2

Please sign in to comment.