| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- // Copyright 2023 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- import Foundation
- // MARK: - URL response error codes
- /// Error code that indicates that the client ID provided was invalid.
- private let kURLResponseErrorCodeInvalidClientID = "auth/invalid-oauth-client-id"
- /// Error code that indicates that a network request within the SFSafariViewController or WKWebView
- /// failed.
- private let kURLResponseErrorCodeNetworkRequestFailed = "auth/network-request-failed"
- /// Error code that indicates that an internal error occurred within the
- /// SFSafariViewController or WKWebView failed.
- private let kURLResponseErrorCodeInternalError = "auth/internal-error"
- private let kFIRAuthErrorMessageMalformedJWT =
- "Failed to parse JWT. Check the userInfo dictionary for the full token."
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- class AuthErrorUtils: NSObject {
- static let internalErrorDomain = "FIRAuthInternalErrorDomain"
- static let userInfoDeserializedResponseKey = "FIRAuthErrorUserInfoDeserializedResponseKey"
- static let userInfoDataKey = "FIRAuthErrorUserInfoDataKey"
- /// This marker indicates that the server error message contains a detail error message which
- /// should be used instead of the hardcoded client error message.
- private static let kServerErrorDetailMarker = " : "
- static func error(code: SharedErrorCode, userInfo: [String: Any]? = nil) -> Error {
- switch code {
- case let .public(publicCode):
- var errorUserInfo: [String: Any] = userInfo ?? [:]
- if errorUserInfo[NSLocalizedDescriptionKey] == nil {
- errorUserInfo[NSLocalizedDescriptionKey] = publicCode.errorDescription
- }
- if let localizedDescription = errorUserInfo[NSLocalizedDescriptionKey] as? String,
- localizedDescription == "" {
- errorUserInfo[NSLocalizedDescriptionKey] = publicCode.errorDescription
- }
- errorUserInfo[AuthErrors.userInfoNameKey] = publicCode.errorCodeString
- return NSError(
- domain: AuthErrors.domain,
- code: publicCode.rawValue,
- userInfo: errorUserInfo
- )
- case let .internal(internalCode):
- // This is an internal error. Wrap it in an internal error.
- let error = NSError(
- domain: internalErrorDomain,
- code: internalCode.rawValue,
- userInfo: userInfo
- )
- return self.error(code: .public(.internalError), underlyingError: error)
- }
- }
- static func error(code: SharedErrorCode, underlyingError: Error?) -> Error {
- var errorUserInfo: [String: Any]?
- if let underlyingError = underlyingError {
- errorUserInfo = [NSUnderlyingErrorKey: underlyingError]
- }
- return error(code: code, userInfo: errorUserInfo)
- }
- static func error(code: AuthErrorCode, underlyingError: Error?) -> Error {
- error(code: SharedErrorCode.public(code), underlyingError: underlyingError)
- }
- static func error(code: AuthErrorCode, userInfo: [String: Any]? = nil) -> Error {
- error(code: SharedErrorCode.public(code), userInfo: userInfo)
- }
- static func error(code: AuthErrorCode, message: String?) -> Error {
- let userInfo: [String: Any]?
- if let message {
- userInfo = [NSLocalizedDescriptionKey: message]
- } else {
- userInfo = nil
- }
- return error(code: SharedErrorCode.public(code), userInfo: userInfo)
- }
- static func userDisabledError(message: String?) -> Error {
- error(code: .userDisabled, message: message)
- }
- static func wrongPasswordError(message: String?) -> Error {
- error(code: .wrongPassword, message: message)
- }
- static func tooManyRequestsError(message: String?) -> Error {
- error(code: .tooManyRequests, message: message)
- }
- static func invalidCustomTokenError(message: String?) -> Error {
- error(code: .invalidCustomToken, message: message)
- }
- static func customTokenMismatchError(message: String?) -> Error {
- error(code: .customTokenMismatch, message: message)
- }
- static func invalidCredentialError(message: String?) -> Error {
- error(code: .invalidCredential, message: message)
- }
- static func requiresRecentLoginError(message: String?) -> Error {
- error(code: .requiresRecentLogin, message: message)
- }
- static func invalidUserTokenError(message: String?) -> Error {
- error(code: .invalidUserToken, message: message)
- }
- static func invalidEmailError(message: String?) -> Error {
- error(code: .invalidEmail, message: message)
- }
- static func providerAlreadyLinkedError() -> Error {
- error(code: .providerAlreadyLinked)
- }
- static func noSuchProviderError() -> Error {
- error(code: .noSuchProvider)
- }
- static func userTokenExpiredError(message: String?) -> Error {
- error(code: .userTokenExpired, message: message)
- }
- static func userNotFoundError(message: String?) -> Error {
- error(code: .userNotFound, message: message)
- }
- static func invalidAPIKeyError() -> Error {
- error(code: .invalidAPIKey)
- }
- static func userMismatchError() -> Error {
- error(code: .userMismatch)
- }
- static func operationNotAllowedError(message: String?) -> Error {
- error(code: .operationNotAllowed, message: message)
- }
- static func weakPasswordError(serverResponseReason reason: String?) -> Error {
- let userInfo: [String: Any]?
- if let reason, !reason.isEmpty {
- userInfo = [
- NSLocalizedFailureReasonErrorKey: reason,
- ]
- } else {
- userInfo = nil
- }
- return error(code: .weakPassword, userInfo: userInfo)
- }
- static func appNotAuthorizedError() -> Error {
- error(code: .appNotAuthorized)
- }
- static func expiredActionCodeError(message: String?) -> Error {
- error(code: .expiredActionCode, message: message)
- }
- static func invalidActionCodeError(message: String?) -> Error {
- error(code: .invalidActionCode, message: message)
- }
- static func invalidMessagePayloadError(message: String?) -> Error {
- error(code: .invalidMessagePayload, message: message)
- }
- static func invalidSenderError(message: String?) -> Error {
- error(code: .invalidSender, message: message)
- }
- static func invalidRecipientEmailError(message: String?) -> Error {
- error(code: .invalidRecipientEmail, message: message)
- }
- static func missingIosBundleIDError(message: String?) -> Error {
- error(code: .missingIosBundleID, message: message)
- }
- static func missingAndroidPackageNameError(message: String?) -> Error {
- error(code: .missingAndroidPackageName, message: message)
- }
- static func unauthorizedDomainError(message: String?) -> Error {
- error(code: .unauthorizedDomain, message: message)
- }
- static func invalidContinueURIError(message: String?) -> Error {
- error(code: .invalidContinueURI, message: message)
- }
- static func missingContinueURIError(message: String?) -> Error {
- error(code: .missingContinueURI, message: message)
- }
- static func missingEmailError(message: String?) -> Error {
- error(code: .missingEmail, message: message)
- }
- static func missingPhoneNumberError(message: String?) -> Error {
- error(code: .missingPhoneNumber, message: message)
- }
- static func invalidPhoneNumberError(message: String?) -> Error {
- error(code: .invalidPhoneNumber, message: message)
- }
- static func missingVerificationCodeError(message: String?) -> Error {
- error(code: .missingVerificationCode, message: message)
- }
- static func invalidVerificationCodeError(message: String?) -> Error {
- error(code: .invalidVerificationCode, message: message)
- }
- static func missingVerificationIDError(message: String?) -> Error {
- error(code: .missingVerificationID, message: message)
- }
- static func invalidVerificationIDError(message: String?) -> Error {
- error(code: .invalidVerificationID, message: message)
- }
- static func sessionExpiredError(message: String?) -> Error {
- error(code: .sessionExpired, message: message)
- }
- static func missingAppCredential(message: String?) -> Error {
- error(code: .missingAppCredential, message: message)
- }
- static func invalidAppCredential(message: String?) -> Error {
- error(code: .invalidAppCredential, message: message)
- }
- static func quotaExceededError(message: String?) -> Error {
- error(code: .quotaExceeded, message: message)
- }
- static func missingAppTokenError(underlyingError: Error?) -> Error {
- error(code: .missingAppToken, underlyingError: underlyingError)
- }
- static func localPlayerNotAuthenticatedError() -> Error {
- error(code: .localPlayerNotAuthenticated)
- }
- static func gameKitNotLinkedError() -> Error {
- error(code: .gameKitNotLinked)
- }
- static func RPCRequestEncodingError(underlyingError: Error) -> Error {
- error(code: .internal(.RPCRequestEncodingError), underlyingError: underlyingError)
- }
- static func JSONSerializationErrorForUnencodableType() -> Error {
- error(code: .internal(.JSONSerializationError))
- }
- static func JSONSerializationError(underlyingError: Error) -> Error {
- error(code: .internal(.JSONSerializationError), underlyingError: underlyingError)
- }
- static func networkError(underlyingError: Error) -> Error {
- error(code: .networkError, underlyingError: underlyingError)
- }
- static func emailAlreadyInUseError(email: String?) -> Error {
- var userInfo: [String: Any]?
- if let email, !email.isEmpty {
- userInfo = [AuthErrors.userInfoEmailKey: email]
- }
- return error(code: .emailAlreadyInUse, userInfo: userInfo)
- }
- static func credentialAlreadyInUseError(message: String?,
- credential: AuthCredential?,
- email: String?) -> Error {
- var userInfo: [String: Any] = [:]
- if let credential {
- userInfo[AuthErrors.userInfoUpdatedCredentialKey] = credential
- }
- if let email, !email.isEmpty {
- userInfo[AuthErrors.userInfoEmailKey] = email
- }
- if !userInfo.isEmpty {
- return error(code: .credentialAlreadyInUse, userInfo: userInfo)
- }
- return error(code: .credentialAlreadyInUse, message: message)
- }
- static func webContextAlreadyPresentedError(message: String?) -> Error {
- error(code: .webContextAlreadyPresented, message: message)
- }
- static func webContextCancelledError(message: String?) -> Error {
- error(code: .webContextCancelled, message: message)
- }
- static func appVerificationUserInteractionFailure(reason: String?) -> Error {
- let userInfo: [String: Any]?
- if let reason, !reason.isEmpty {
- userInfo = [NSLocalizedFailureReasonErrorKey: reason]
- } else {
- userInfo = nil
- }
- return error(code: .appVerificationUserInteractionFailure, userInfo: userInfo)
- }
- static func webSignInUserInteractionFailure(reason: String?) -> Error {
- let userInfo: [String: Any]?
- if let reason, !reason.isEmpty {
- userInfo = [NSLocalizedFailureReasonErrorKey: reason]
- } else {
- userInfo = nil
- }
- return error(code: .webSignInUserInteractionFailure, userInfo: userInfo)
- }
- static func urlResponseError(code: String, message: String?) -> Error {
- let errorCode: AuthErrorCode
- switch code {
- case kURLResponseErrorCodeInvalidClientID:
- errorCode = .invalidClientID
- case kURLResponseErrorCodeNetworkRequestFailed:
- errorCode = .webNetworkRequestFailed
- case kURLResponseErrorCodeInternalError:
- errorCode = .webInternalError
- default:
- return AuthErrorUtils.webSignInUserInteractionFailure(reason: "[\(code)] - \(message ?? "")")
- }
- return error(code: errorCode, message: message)
- }
- static func nullUserError(message: String?) -> Error {
- error(code: .nullUser, message: message)
- }
- static func invalidProviderIDError(message: String?) -> Error {
- error(code: .invalidProviderID, message: message)
- }
- static func invalidDynamicLinkDomainError(message: String?) -> Error {
- error(code: .invalidDynamicLinkDomain, message: message)
- }
- static func missingOrInvalidNonceError(message: String?) -> Error {
- error(code: .missingOrInvalidNonce, message: message)
- }
- static func keychainError(function: String, status: OSStatus) -> Error {
- let reason = "\(function) (\(status))"
- return error(code: .keychainError, userInfo: [NSLocalizedFailureReasonErrorKey: reason])
- }
- static func tenantIDMismatchError() -> Error {
- error(code: .tenantIDMismatch)
- }
- static func unsupportedTenantOperationError() -> Error {
- error(code: .unsupportedTenantOperation)
- }
- static func notificationNotForwardedError() -> Error {
- error(code: .notificationNotForwarded)
- }
- static func appNotVerifiedError(message: String?) -> Error {
- error(code: .appNotVerified, message: message)
- }
- static func missingClientIdentifierError(message: String?) -> Error {
- error(code: .missingClientIdentifier, message: message)
- }
- static func missingClientType(message: String?) -> Error {
- error(code: .missingClientType, message: message)
- }
- static func captchaCheckFailedError(message: String?) -> Error {
- error(code: .captchaCheckFailed, message: message)
- }
- static func unexpectedResponse(data: Data?, underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [:]
- if let data {
- userInfo[userInfoDataKey] = data
- }
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(code: .internal(.unexpectedResponse), userInfo: userInfo)
- }
- static func unexpectedErrorResponse(data: Data?,
- underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [:]
- if let data {
- userInfo[userInfoDataKey] = data
- }
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(code: .internal(.unexpectedErrorResponse), userInfo: userInfo)
- }
- static func unexpectedErrorResponse(deserializedResponse: Any?) -> Error {
- var userInfo: [String: Any]?
- if let deserializedResponse {
- userInfo = [userInfoDeserializedResponseKey: deserializedResponse]
- }
- return error(code: .internal(.unexpectedErrorResponse), userInfo: userInfo)
- }
- static func unexpectedResponse(deserializedResponse: Any?) -> Error {
- var userInfo: [String: Any]?
- if let deserializedResponse {
- userInfo = [userInfoDeserializedResponseKey: deserializedResponse]
- }
- return error(code: .internal(.unexpectedResponse), userInfo: userInfo)
- }
- static func unexpectedResponse(deserializedResponse: Any?,
- underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [:]
- if let deserializedResponse {
- userInfo[userInfoDeserializedResponseKey] = deserializedResponse
- }
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(code: .internal(.unexpectedResponse), userInfo: userInfo)
- }
- static func unexpectedErrorResponse(deserializedResponse: Any?,
- underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [:]
- if let deserializedResponse {
- userInfo[userInfoDeserializedResponseKey] = deserializedResponse
- }
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(
- code: .internal(.unexpectedErrorResponse),
- userInfo: userInfo.isEmpty ? nil : userInfo
- )
- }
- static func malformedJWTError(token: String, underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [
- NSLocalizedDescriptionKey: kFIRAuthErrorMessageMalformedJWT,
- userInfoDataKey: token,
- ]
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(code: .malformedJWT, userInfo: userInfo)
- }
- static func RPCResponseDecodingError(deserializedResponse: Any?,
- underlyingError: Error?) -> Error {
- var userInfo: [String: Any] = [:]
- if let deserializedResponse {
- userInfo[userInfoDeserializedResponseKey] = deserializedResponse
- }
- if let underlyingError {
- userInfo[NSUnderlyingErrorKey] = underlyingError
- }
- return error(code: .internal(.RPCResponseDecodingError), userInfo: userInfo)
- }
- static func accountExistsWithDifferentCredentialError(email: String?,
- updatedCredential: AuthCredential?)
- -> Error {
- var userInfo: [String: Any] = [:]
- if let email {
- userInfo[AuthErrors.userInfoEmailKey] = email
- }
- if let updatedCredential {
- userInfo[AuthErrors.userInfoUpdatedCredentialKey] = updatedCredential
- }
- return error(code: .accountExistsWithDifferentCredential, userInfo: userInfo)
- }
- static func blockingCloudFunctionServerResponse(message: String?) -> Error {
- guard let message else {
- return error(code: .blockingCloudFunctionError, message: message)
- }
- var jsonString = message.replacingOccurrences(
- of: "HTTP Cloud Function returned an error:",
- with: ""
- )
- jsonString = jsonString.trimmingCharacters(in: .whitespaces)
- let jsonData = jsonString.data(using: .utf8) ?? Data()
- do {
- let jsonDict = try JSONSerialization
- .jsonObject(with: jsonData, options: []) as? [String: Any] ?? [:]
- let errorDict = jsonDict["error"] as? [String: Any] ?? [:]
- let errorMessage = errorDict["message"] as? String
- return error(code: .blockingCloudFunctionError, message: errorMessage)
- } catch {
- return JSONSerializationError(underlyingError: error)
- }
- }
- #if os(iOS)
- static func secondFactorRequiredError(pendingCredential: String?,
- hints: [MultiFactorInfo],
- auth: Auth)
- -> Error {
- var userInfo: [String: Any] = [:]
- if let pendingCredential = pendingCredential {
- let resolver = MultiFactorResolver(with: pendingCredential, hints: hints, auth: auth)
- userInfo[AuthErrors.userInfoMultiFactorResolverKey] = resolver
- }
- return error(code: .secondFactorRequired, userInfo: userInfo)
- }
- #endif // os(iOS)
- static func recaptchaSDKNotLinkedError() -> Error {
- // TODO(ObjC): point the link to GCIP doc once available.
- let message = "The reCAPTCHA SDK is not linked to your app. See " +
- "https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps"
- return error(code: .recaptchaSDKNotLinked, message: message)
- }
- static func recaptchaSiteKeyMissing() -> Error {
- // TODO(ObjC): point the link to GCIP doc once available.
- let message = "The site key for the reCAPTCHA SDK was not found. See " +
- "https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps"
- return error(code: .recaptchaSiteKeyMissing, message: message)
- }
- static func recaptchaActionCreationFailed() -> Error {
- // TODO(ObjC): point the link to GCIP doc once available.
- let message = "The reCAPTCHA SDK action class creation failed. See " +
- "https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps"
- return error(code: .recaptchaActionCreationFailed, message: message)
- }
- }
- protocol MultiFactorResolverWrapper: NSObjectProtocol {}
|