Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,14 @@ SampleApps/PingExample/PingExample/ConfigurationManager_LOCAL_78086.swift
SampleApps/PingExample/PingExample/ConfigurationManager_REMOTE_78086.swift
/Android/
GEMINI.MD

# Local agent / planning artefacts (not shipped with the SDK)
.claude/
ai-tasks/
CLAUDE.md
AGENTS.md
.github/copilot-instructions.md

# Merge / Finder leftovers
*.orig
**/* copy.xcodeproj/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [Unreleased]
#### Added
- Routed FIDO ceremony logs through the workflow logger by adding a `logger:` parameter to `Fido.register` and `Fido.authenticate`. The DaVinci collectors and Journey callbacks pass the workflow's configured logger so FIDO ceremony state transitions and errors emit through the same logger as the surrounding flow [SDKS-4924]

## [2.0.0]
#### Added
- Added new `PingJourney` module [SDKS-3918]
Expand Down
6 changes: 6 additions & 0 deletions Fido/Fido.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
EC5C81B42ECF6DE400C91570 /* PingDavinciPlugin.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EC5C81B22ECF6DE400C91570 /* PingDavinciPlugin.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
EC5C81F82ED0920300C91570 /* PingJourney.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC5C81F72ED0920300C91570 /* PingJourney.framework */; };
EC5C81F92ED0920300C91570 /* PingJourney.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EC5C81F72ED0920300C91570 /* PingJourney.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
ECD780722EA251010050E60F /* PingDavinci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 955861222EB0657300DD55A6 /* PingDavinci.framework */; };
ECA2E7DE2ED740E000C32766 /* FidoRegistrationCollectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA2E7DD2ED740E000C32766 /* FidoRegistrationCollectorTests.swift */; };
ECA2E7DF2ED740E000C32766 /* FidoAuthenticationCollectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA2E7DC2ED740E000C32766 /* FidoAuthenticationCollectorTests.swift */; };
ECD5A2F72E8FE49C00A27205 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = ECD5A2F62E8FE49C00A27205 /* PrivacyInfo.xcprivacy */; };
Expand All @@ -35,6 +36,7 @@
ECD780602EA24FFA0050E60F /* FidoCallbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD780512EA24FC40050E60F /* FidoCallbackTests.swift */; };
ECD780612EA24FFF0050E60F /* FidoCollectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD780522EA24FC40050E60F /* FidoCollectorTests.swift */; };
ECD780642EA2500E0050E60F /* Mocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD780552EA24FC40050E60F /* Mocks.swift */; };
ECD780712EA251000050E60F /* FidoLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD780702EA251000050E60F /* FidoLoggerTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -105,6 +107,7 @@
ECD780522EA24FC40050E60F /* FidoCollectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FidoCollectorTests.swift; sourceTree = "<group>"; };
ECD780552EA24FC40050E60F /* Mocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mocks.swift; sourceTree = "<group>"; };
ECD780562EA24FC40050E60F /* PingFidoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingFidoTests.swift; sourceTree = "<group>"; };
ECD780702EA251000050E60F /* FidoLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FidoLoggerTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -124,6 +127,7 @@
files = (
ECD7804A2EA24F1B0050E60F /* PingFido.framework in Frameworks */,
EC5C81F82ED0920300C91570 /* PingJourney.framework in Frameworks */,
ECD780722EA251010050E60F /* PingDavinci.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -208,6 +212,7 @@
ECA2E7DD2ED740E000C32766 /* FidoRegistrationCollectorTests.swift */,
ECD780512EA24FC40050E60F /* FidoCallbackTests.swift */,
ECD780522EA24FC40050E60F /* FidoCollectorTests.swift */,
ECD780702EA251000050E60F /* FidoLoggerTests.swift */,
ECD780552EA24FC40050E60F /* Mocks.swift */,
ECD780562EA24FC40050E60F /* PingFidoTests.swift */,
);
Expand Down Expand Up @@ -354,6 +359,7 @@
ECD780612EA24FFF0050E60F /* FidoCollectorTests.swift in Sources */,
ECD780602EA24FFA0050E60F /* FidoCallbackTests.swift in Sources */,
ECD780642EA2500E0050E60F /* Mocks.swift in Sources */,
ECD780712EA251000050E60F /* FidoLoggerTests.swift in Sources */,
ECA2E7DE2ED740E000C32766 /* FidoRegistrationCollectorTests.swift in Sources */,
ECA2E7DF2ED740E000C32766 /* FidoAuthenticationCollectorTests.swift in Sources */,
);
Expand Down
34 changes: 17 additions & 17 deletions Fido/Fido/Davinci/AbstractFidoCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// AbstractFidoCollector.swift
// Fido
//
// Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
// Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -54,8 +54,8 @@ public class AbstractFidoCollector: AnyFieldCollector, DaVinciAware, Submittable
public var davinci: DaVinci?

/// The logger for recording Fido related events.
public var logger: Logger? {
return davinci?.config.logger
public var logger: Logger {
Comment thread
george-bafaloukas-forgerock marked this conversation as resolved.
return davinci?.config.logger ?? LogManager.logger
}

/// Private storage for the Fido instance
Expand Down Expand Up @@ -100,31 +100,31 @@ public class AbstractFidoCollector: AnyFieldCollector, DaVinciAware, Submittable
/// - Parameter error: The error to handle and transform.
/// - Returns: A transformed `FidoError` that is more human-readable and spec-compliant.
public func handleError(error: Error) -> FidoError {
logger?.e("Handling FIDO error: \(error.localizedDescription)", error: error)
logger.e("Handling FIDO error: \(error.localizedDescription)", error: error)

// Check if it's a FidoError first
if let fidoError = error as? FidoError {
switch fidoError {
case .timeout:
logger?.d("FIDO operation timed out")
logger.d("FIDO operation timed out")
return .timeout
case .unsupportedAction(let message):
logger?.d("FIDO ERROR NOT SUPPORTED: \(message)")
logger.d("FIDO ERROR NOT SUPPORTED: \(message)")
return .unsupportedAction(message)
case .invalidResponse:
logger?.d("FIDO invalid response")
logger.d("FIDO invalid response")
return .invalidResponse
case .invalidChallenge:
logger?.d("FIDO invalid challenge")
logger.d("FIDO invalid challenge")
return .invalidChallenge
case .invalidWindow:
logger?.d("FIDO invalid window")
logger.d("FIDO invalid window")
return .invalidWindow
case .invalidAction:
logger?.d("FIDO invalid action")
logger.d("FIDO invalid action")
return .invalidAction
case .missingParameters(let message):
logger?.d("FIDO missing parameters: \(message)")
logger.d("FIDO missing parameters: \(message)")
return .missingParameters(message)
}
}
Expand All @@ -135,23 +135,23 @@ public class AbstractFidoCollector: AnyFieldCollector, DaVinciAware, Submittable
case ASAuthorizationError.errorDomain:
switch nsError.code {
case ASAuthorizationError.canceled.rawValue:
logger?.d("Credential operation cancelled")
logger.d("Credential operation cancelled")
return .unsupportedAction(FidoConstants.ERROR_NOT_ALLOWED_MESSAGE)
case ASAuthorizationError.invalidResponse.rawValue:
logger?.d("DOM exception occurred: InvalidStateError")
logger.d("DOM exception occurred: InvalidStateError")
return .invalidResponse
case ASAuthorizationError.notHandled.rawValue:
logger?.d("DOM exception occurred: NotSupportedError")
logger.d("DOM exception occurred: NotSupportedError")
return .unsupportedAction("Operation not supported")
case ASAuthorizationError.unknown.rawValue:
logger?.d("Unknown error occurred")
logger.d("Unknown error occurred")
return .unsupportedAction("Unknown error: \(error.localizedDescription)")
default:
logger?.d("Unknown authorization error occurred")
logger.d("Unknown authorization error occurred")
return .unsupportedAction("Unknown error: \(error.localizedDescription)")
}
default:
logger?.d("Unknown error occurred")
logger.d("Unknown error occurred")
return .unsupportedAction("Unknown error: \(error.localizedDescription)")
}
}
Expand Down
29 changes: 15 additions & 14 deletions Fido/Fido/Davinci/FidoAuthenticationCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,24 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
/// - Throws: An error if the required `publicKeyCredentialRequestOptions` parameter is missing from the JSON.
required public init(with json: [String : Any]) {
super.init(with: json)
logger?.d("Initializing Fido authentication collector")
logger.d("Initializing Fido authentication collector")
guard let options = json[FidoConstants.FIELD_PUBLIC_KEY_CREDENTIAL_REQUEST_OPTIONS] as? [String: Any] else {
logger?.e("Missing \(FidoConstants.FIELD_PUBLIC_KEY_CREDENTIAL_REQUEST_OPTIONS)", error: nil)
logger.e("Missing \(FidoConstants.FIELD_PUBLIC_KEY_CREDENTIAL_REQUEST_OPTIONS)", error: nil)
return
}
self.publicKeyCredentialRequestOptions = self.transform(options)
logger?.d("Fido authentication collector initialized with request options")
logger.d("Fido authentication collector initialized with request options")
}

/// The payload to be sent to the DaVinci server.
///
/// - Returns: A dictionary containing the assertion value, or `nil` if authentication has not been completed.
override public func payload() -> [String: Any]? {
guard let assertionValue = assertionValue else {
logger?.d("No assertion value available, returning null payload")
logger.d("No assertion value available, returning null payload")
return nil
}
logger?.d("Returning assertion payload for Fido authentication")
logger.d("Returning assertion payload for Fido authentication")
return [FidoConstants.FIELD_ASSERTION_VALUE: assertionValue]
}

Expand All @@ -67,14 +67,15 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
/// - Throws: An error if the authentication process fails or the response is invalid.
@MainActor
public func authenticate(window: ASPresentationAnchor) async -> Result<[String: Any], Error> {
logger?.d("Starting FIDO authentication (async Result)")
logger.d("Starting FIDO authentication (async Result)")

do {
// 1. Wrap the closure-based fido.authenticate in a continuation
// This still throws internally within the 'do' block if the continuation resumes with an error.
let response: [String: Any] = try await withUnsafeThrowingContinuation { continuation in
// Assuming 'fido' instance is accessible
fido.authenticate(options: publicKeyCredentialRequestOptions, window: window) { [continuation] result in
// Pass the workflow logger so the underlying ASAuthorization ceremony
// emits log messages through the same logger as the surrounding flow.
fido.authenticate(options: publicKeyCredentialRequestOptions, window: window, logger: logger) { [continuation] result in
Task {
await MainActor.run {
nonisolated(unsafe) let sendableResult = result
Expand All @@ -85,7 +86,7 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
}

// 2. Process the successful response data extraction
logger?.d("FIDO authentication successful, building assertionValue object...")
logger.d("FIDO authentication successful, building assertionValue object...")

guard let signatureData = response[FidoConstants.FIELD_SIGNATURE] as? Data,
let clientData = response[FidoConstants.FIELD_CLIENT_DATA_JSON] as? Data,
Expand All @@ -94,7 +95,7 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
let userHandleData = response[FidoConstants.FIELD_USER_HANDLE] as? Data else {

let error = FidoError.invalidResponse
logger?.e(error.localizedDescription, error: error)
logger.e(error.localizedDescription, error: error)
let transformedError = self.handleError(error: error)
return .failure(transformedError) // Return failure with transformed error
}
Expand All @@ -114,15 +115,15 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
]
]

logger?.d("assertionValue object created successfully")
logger.d("assertionValue object created successfully")
self.assertionValue = newAssertionValue // Store the value (side effect)

// 4. Return success with the constructed assertionValue
return .success(newAssertionValue)

} catch {
// 5. Handle any error caught from the continuation
logger?.e("FIDO authentication failed", error: error)
logger.e("FIDO authentication failed", error: error)
let transformedError = self.handleError(error: error)
return .failure(transformedError) // Return failure with transformed error
}
Expand All @@ -134,7 +135,7 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
/// - Parameter input: The dictionary of options received from the server.
/// - Returns: A transformed dictionary of options.
private func transform(_ input: [String: Any]) -> [String: Any] {
logger?.d("Transforming FIDO authentication request options")
logger.d("Transforming FIDO authentication request options")
var output = input

if let challenge = output[FidoConstants.FIELD_CHALLENGE] as? [Int] {
Expand All @@ -159,7 +160,7 @@ public class FidoAuthenticationCollector: AbstractFidoCollector, Closeable, @unc
output[FidoConstants.FIELD_ALLOW_CREDENTIALS] = updatedCredentials
}

logger?.d("FIDO authentication request options transformed successfully")
logger.d("FIDO authentication request options transformed successfully")
return output
}
}
Expand Down
Loading
Loading