Skip to content

Commit

Permalink
Finalize event parameters, remove initial success log
Browse files Browse the repository at this point in the history
  • Loading branch information
mats-stripe committed Jan 22, 2025
1 parent 41f6062 commit 10af40e
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 63 deletions.
20 changes: 10 additions & 10 deletions StripeCore/StripeCore/Source/Attestation/StripeAttest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,25 +113,25 @@ import UIKit
}
}

@_spi(STP) public enum AttestationError: Error {
@_spi(STP) public enum AttestationError: String, Error {
/// Attestation is not supported on this device.
case attestationNotSupported
case attestationNotSupported = "attestation_not_supported"
/// Device ID is unavailable.
case noDeviceID
case noDeviceID = "no_device_id"
/// App ID is unavailable.
case noAppID
case noAppID = "no_app_id"
/// Retried assertion, but it failed.
case secondAssertionFailureAfterRetryingAttestation
case secondAssertionFailureAfterRetryingAttestation = "second_assertion_failure_after_retrying_attestation"
/// Can't attest any more keys today.
case attestationRateLimitExceeded
case attestationRateLimitExceeded = "attestation_rate_limit_exceeded"
/// The challenge couldn't be converted to UTF-8 data.
case invalidChallengeData
case invalidChallengeData = "invalid_challenge_data"
/// The backend asked us not to attest
case shouldNotAttest
case shouldNotAttest = "should_not_attest"
/// The backend asked us to attest, but the key is already attested
case shouldAttestButKeyIsAlreadyAttested
case shouldAttestButKeyIsAlreadyAttested = "should_attest_but_key_is_already_attested"
/// A publishable key was not set
case noPublishableKey
case noPublishableKey = "no_publishable_key"
}

// MARK: - Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,36 @@ final class FinancialConnectionsAPIClient {
/// In case of an assertion error, returns the unmodified base parameters
func assertAndApplyAttestationParameters(
to baseParameters: [String: Any],
api: FinancialConnectionsAPIClientLogger.API,
pane: FinancialConnectionsSessionManifest.NextPane
) -> Future<[String: Any]> {
let promise = Promise<[String: Any]>()
Task {
do {
let attest = backingAPIClient.stripeAttest
let handle = try await attest.assert()
logger.log(.attestationRequestTokenSucceeded, pane: pane)
logger.log(.attestationRequestTokenSucceeded(api), pane: pane)
let newParameters = baseParameters.merging(handle.assertion.requestFields) { (_, new) in new }
promise.resolve(with: newParameters)
} catch {
// Fail silently if we can't get an assertion, we'll try the request anyway. It may fail.
logger.log(.attestationRequestTokenFailed, pane: pane)
logger.log(.attestationRequestTokenFailed(api, error), pane: pane)
promise.resolve(with: baseParameters)
}
}
return promise
}

/// Marks the assertion as completed and forwards attestation errors to the `StripeAttest` client for logging.
func completeAssertion(possibleError: Error?, pane: FinancialConnectionsSessionManifest.NextPane) {
func completeAssertion(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API,
pane: FinancialConnectionsSessionManifest.NextPane
) {
let attest = backingAPIClient.stripeAttest
Task {
if let error = possibleError, StripeAttest.isLinkAssertionError(error: error) {
logger.log(.attestationVerdictFailed, pane: pane)
logger.log(.attestationVerdictFailed(api), pane: pane)
await attest.receivedAssertionError(error)
}
await attest.assertionCompleted()
Expand Down Expand Up @@ -147,6 +152,7 @@ protocol FinancialConnectionsAPI {

func completeAssertion(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API,
pane: FinancialConnectionsSessionManifest.NextPane
)

Expand Down Expand Up @@ -381,7 +387,6 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI {
if attest.isSupported {
mobileParameters["supports_app_verification"] = true
mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier
logger.log(.attestationInitSucceeded, pane: .consent)
} else {
logger.log(.attestationInitFailed, pane: .consent)
}
Expand Down Expand Up @@ -949,17 +954,20 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI {
parameters["request_surface"] = requestSurface
parameters["session_id"] = sessionId
parameters["email_source"] = emailSource.rawValue
return assertAndApplyAttestationParameters(to: parameters, pane: pane)
.chained { [weak self] updatedParameters in
guard let self else {
return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated."))
}
return self.post(
resource: APIMobileEndpointConsumerSessionLookup,
parameters: updatedParameters,
useConsumerPublishableKeyIfNeeded: false
)
return assertAndApplyAttestationParameters(
to: parameters,
api: .consumerSessionLookup,
pane: pane
).chained { [weak self] updatedParameters in
guard let self else {
return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated."))
}
return self.post(
resource: APIMobileEndpointConsumerSessionLookup,
parameters: updatedParameters,
useConsumerPublishableKeyIfNeeded: false
)
}
} else {
parameters["client_secret"] = clientSecret
return post(
Expand Down Expand Up @@ -1060,16 +1068,19 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI {
}

if useMobileEndpoints {
return assertAndApplyAttestationParameters(to: parameters, pane: pane)
.chained { [weak self] updatedParameters in
guard let self else {
return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated."))
}
return self.post(
resource: APIMobileEndpointLinkAccountSignUp,
parameters: updatedParameters,
useConsumerPublishableKeyIfNeeded: false
)
return assertAndApplyAttestationParameters(
to: parameters,
api: .linkSignUp,
pane: pane
).chained { [weak self] updatedParameters in
guard let self else {
return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated."))
}
return self.post(
resource: APIMobileEndpointLinkAccountSignUp,
parameters: updatedParameters,
useConsumerPublishableKeyIfNeeded: false
)
}
} else {
return post(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@
//

import Foundation
@_spi(STP) import StripeCore

struct FinancialConnectionsAPIClientLogger {
private var analyticsClient = FinancialConnectionsAnalyticsClient()

enum API: String {
case consumerSessionLookup = "consumer_session_lookup"
case linkSignUp = "link_sign_up"
}

enum Event {
/// When checking if generating attestation is supported succeeds.
case attestationInitSucceeded
/// When checking if generating attestation is supported does not succeed.
case attestationInitFailed
/// When an attestation token gets generated successfully.
case attestationRequestTokenSucceeded
case attestationRequestTokenSucceeded(API)
/// When a token generation attempt fails client-side.
case attestationRequestTokenFailed
case attestationRequestTokenFailed(API, Error)
/// When an attestation verdict fails backend side and we get an attestation related error.
case attestationVerdictFailed
case attestationVerdictFailed(API)

var name: String {
switch self {
case .attestationInitSucceeded:
return "attestation.init_succeeded"
case .attestationInitFailed:
return "attestation.init_failed"
case .attestationRequestTokenSucceeded:
Expand All @@ -49,8 +51,19 @@ struct FinancialConnectionsAPIClientLogger {
reason = "ios_os_version_unsupported"
}
return ["reason": reason]
default:
return [:]
case .attestationRequestTokenFailed(let api, let error):
var errorReason: String
if let attestationError = error as? StripeAttest.AttestationError {
errorReason = attestationError.rawValue
} else {
errorReason = "unknown"
}
return [
"api": api.rawValue,
"error_reason": errorReason,
]
case .attestationRequestTokenSucceeded(let api), .attestationVerdictFailed(let api):
return ["api": api.rawValue]
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ final class FinancialConnectionsAsyncAPIClient {
}

/// Marks the assertion as completed and forwards attestation errors to the `StripeAttest` client for logging.
func completeAssertion(possibleError: Error?, pane: FinancialConnectionsSessionManifest.NextPane) {
func completeAssertion(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API,
pane: FinancialConnectionsSessionManifest.NextPane
) {
let attest = backingAPIClient.stripeAttest
Task {
if let error = possibleError, StripeAttest.isLinkAssertionError(error: error) {
logger.log(.attestationVerdictFailed, pane: pane)
logger.log(.attestationVerdictFailed(api), pane: pane)
await attest.receivedAssertionError(error)
}
await attest.assertionCompleted()
Expand All @@ -60,17 +64,18 @@ final class FinancialConnectionsAsyncAPIClient {
/// In case of an assertion error, returns the unmodified base parameters
func assertAndApplyAttestationParameters(
to baseParameters: [String: Any],
api: FinancialConnectionsAPIClientLogger.API,
pane: FinancialConnectionsSessionManifest.NextPane
) async -> [String: Any] {
do {
let attest = backingAPIClient.stripeAttest
let handle = try await attest.assert()
logger.log(.attestationRequestTokenSucceeded, pane: pane)
logger.log(.attestationRequestTokenSucceeded(api), pane: pane)
let newParameters = baseParameters.merging(handle.assertion.requestFields) { (_, new) in new }
return newParameters
} catch {
// Fail silently if we can't get an assertion, we'll try the request anyway. It may fail.
logger.log(.attestationRequestTokenFailed, pane: pane)
logger.log(.attestationRequestTokenFailed(api, error), pane: pane)
return baseParameters
}
}
Expand Down Expand Up @@ -383,7 +388,6 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI {
if attest.isSupported {
mobileParameters["supports_app_verification"] = true
mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier
logger.log(.attestationInitSucceeded, pane: .consent)
} else {
logger.log(.attestationInitFailed, pane: .consent)
}
Expand Down Expand Up @@ -847,7 +851,11 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI {
parameters["request_surface"] = requestSurface
parameters["session_id"] = sessionId
parameters["email_source"] = emailSource.rawValue
let updatedParameters = await assertAndApplyAttestationParameters(to: parameters, pane: pane)
let updatedParameters = await assertAndApplyAttestationParameters(
to: parameters,
api: .consumerSessionLookup,
pane: pane
)
return try await post(endpoint: .mobileConsumerSessionLookup, parameters: updatedParameters)
} else {
parameters["client_secret"] = clientSecret
Expand Down Expand Up @@ -946,7 +954,11 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI {
}
}
if useMobileEndpoints {
let updatedParameters = await assertAndApplyAttestationParameters(to: parameters, pane: pane)
let updatedParameters = await assertAndApplyAttestationParameters(
to: parameters,
api: .linkSignUp,
pane: pane
)
return try await post(endpoint: .mobileLinkAccountSignup, parameters: updatedParameters)
} else {
return try await post(endpoint: .linkAccountsSignUp, parameters: parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ protocol LinkLoginDataSource: AnyObject {
func attachToAccountAndSynchronize(
with linkSignUpResponse: LinkSignUpResponse
) -> Future<FinancialConnectionsSynchronize>
func completeAssertionIfNeeded(possibleError: Error?)
func completeAssertionIfNeeded(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API
)
}

final class LinkLoginDataSourceImplementation: LinkLoginDataSource {
Expand Down Expand Up @@ -127,10 +130,14 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource {
}

// Marks the assertion as completed and logs possible errors during verified flows.
func completeAssertionIfNeeded(possibleError: Error?) {
func completeAssertionIfNeeded(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API
) {
guard manifest.verified else { return }
apiClient.completeAssertion(
possibleError: possibleError,
api: api,
pane: .linkLogin
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ final class LinkLoginViewController: UIViewController {
footerButton?.isLoading = false

guard let self else { return }
self.dataSource.completeAssertionIfNeeded(possibleError: result.error)
self.dataSource.completeAssertionIfNeeded(
possibleError: result.error,
api: .consumerSessionLookup
)

switch result {
case .success(let response):
Expand Down Expand Up @@ -214,7 +217,10 @@ final class LinkLoginViewController: UIViewController {
.observe { [weak self] result in
guard let self else { return }
self.footerButton?.isLoading = false
self.dataSource.completeAssertionIfNeeded(possibleError: result.error)
self.dataSource.completeAssertionIfNeeded(
possibleError: result.error,
api: .linkSignUp
)

switch result {
case .success(let response):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ protocol NetworkingLinkSignupDataSource: AnyObject {
phoneNumber: String,
countryCode: String
) -> Future<String?>
func completeAssertionIfNeeded(possibleError: Error?)
func completeAssertionIfNeeded(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API
)
}

final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDataSource {
Expand Down Expand Up @@ -130,10 +133,14 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa
}

// Marks the assertion as completed and logs possible errors during verified flows.
func completeAssertionIfNeeded(possibleError: Error?) {
func completeAssertionIfNeeded(
possibleError: Error?,
api: FinancialConnectionsAPIClientLogger.API
) {
guard manifest.verified else { return }
apiClient.completeAssertion(
possibleError: possibleError,
api: api,
pane: .networkingLinkSignupPane
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ final class NetworkingLinkSignupViewController: UIViewController {
)
.observe { [weak self] result in
guard let self = self else { return }
self.dataSource.completeAssertionIfNeeded(possibleError: result.error)
self.dataSource.completeAssertionIfNeeded(
possibleError: result.error,
api: .linkSignUp
)

switch result {
case .success(let customSuccessPaneMessage):
Expand Down Expand Up @@ -298,7 +301,10 @@ extension NetworkingLinkSignupViewController: LinkSignupFormViewDelegate {
)
.observe { [weak self, weak bodyFormView] result in
guard let self = self else { return }
self.dataSource.completeAssertionIfNeeded(possibleError: result.error)
self.dataSource.completeAssertionIfNeeded(
possibleError: result.error,
api: .consumerSessionLookup
)

switch result {
case .success(let response):
Expand Down
Loading

0 comments on commit 10af40e

Please sign in to comment.