Skip to content

Commit 3e2c63d

Browse files
committed
add linkDomain support on Firebase Auth, including error codes and sample app change
1 parent 1077879 commit 3e2c63d

File tree

13 files changed

+73
-2
lines changed

13 files changed

+73
-2
lines changed

FirebaseAuth/Sources/Swift/ActionCode/ActionCodeSettings.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ import Foundation
4242
/// The Firebase Dynamic Link domain used for out of band code flow.
4343
@objc open var dynamicLinkDomain: String?
4444

45+
/// The out of band custom domain for handling code in app.
46+
@objc open var linkDomain: String?
47+
4548
/// Sets the iOS bundle ID.
4649
@objc override public init() {
4750
iOSBundleID = Bundle.main.bundleIdentifier

FirebaseAuth/Sources/Swift/Backend/AuthBackend.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
449449
.missingAppCredential(message: serverDetailErrorMessage)
450450
case "INVALID_CODE": return AuthErrorUtils
451451
.invalidVerificationCodeError(message: serverDetailErrorMessage)
452+
case "INVALID_HOSTING_LINK_DOMAIN": return AuthErrorUtils
453+
.invalidHostingLinkDomainError(message: serverDetailErrorMessage)
452454
case "INVALID_SESSION_INFO": return AuthErrorUtils
453455
.invalidVerificationIDError(message: serverDetailErrorMessage)
454456
case "SESSION_EXPIRED": return AuthErrorUtils

FirebaseAuth/Sources/Swift/Backend/RPC/GetOOBConfirmationCodeRequest.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ private let kCanHandleCodeInAppKey = "canHandleCodeInApp"
7878
/// The key for the "dynamic link domain" value in the request.
7979
private let kDynamicLinkDomainKey = "dynamicLinkDomain"
8080

81+
/// The key for the "link domain" value in the request.
82+
private let kLinkDomainKey = "linkDomain"
83+
8184
/// The value for the "PASSWORD_RESET" request type.
8285
private let kPasswordResetRequestTypeValue = "PASSWORD_RESET"
8386

@@ -140,6 +143,9 @@ class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
140143
/// The Firebase Dynamic Link domain used for out of band code flow.
141144
private(set) var dynamicLinkDomain: String?
142145

146+
/// The Firebase Hosting domain used for out of band code flow.
147+
private(set) var linkDomain: String?
148+
143149
/// Response to the captcha.
144150
var captchaResponse: String?
145151

@@ -172,6 +178,7 @@ class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
172178
androidInstallApp = actionCodeSettings?.androidInstallIfNotAvailable ?? false
173179
handleCodeInApp = actionCodeSettings?.handleCodeInApp ?? false
174180
dynamicLinkDomain = actionCodeSettings?.dynamicLinkDomain
181+
linkDomain = actionCodeSettings?.linkDomain
175182

176183
super.init(
177184
endpoint: kGetOobConfirmationCodeEndpoint,
@@ -274,6 +281,9 @@ class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
274281
if let dynamicLinkDomain {
275282
body[kDynamicLinkDomainKey] = dynamicLinkDomain
276283
}
284+
if let linkDomain {
285+
body[kLinkDomainKey] = linkDomain
286+
}
277287
if let captchaResponse {
278288
body[kCaptchaResponseKey] = captchaResponse
279289
}

FirebaseAuth/Sources/Swift/Utilities/AuthErrorUtils.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ class AuthErrorUtils {
366366
error(code: .invalidDynamicLinkDomain, message: message)
367367
}
368368

369+
static func invalidHostingLinkDomainError(message: String?) -> Error {
370+
error(code: .invalidHostingLinkDomain, message: message)
371+
}
372+
369373
static func missingOrInvalidNonceError(message: String?) -> Error {
370374
error(code: .missingOrInvalidNonce, message: message)
371375
}

FirebaseAuth/Sources/Swift/Utilities/AuthErrors.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ import Foundation
258258
/// unauthorized for the current project.
259259
case invalidDynamicLinkDomain = 17074
260260

261+
/// Indicates that the provided Firebase Hosting Link domain is not owned by the current project.
262+
case invalidHostingLinkDomain = 17214
263+
261264
/// Indicates that the credential is rejected because it's malformed or mismatching.
262265
case rejectedCredential = 17075
263266

@@ -468,6 +471,8 @@ import Foundation
468471
return kErrorInvalidProviderID
469472
case .invalidDynamicLinkDomain:
470473
return kErrorInvalidDynamicLinkDomain
474+
case .invalidHostingLinkDomain:
475+
return kErrorInvalidHostingLinkDomain
471476
case .webInternalError:
472477
return kErrorWebInternalError
473478
case .webSignInUserInteractionFailure:
@@ -661,6 +666,8 @@ import Foundation
661666
return "ERROR_INVALID_PROVIDER_ID"
662667
case .invalidDynamicLinkDomain:
663668
return "ERROR_INVALID_DYNAMIC_LINK_DOMAIN"
669+
case .invalidHostingLinkDomain:
670+
return "ERROR_INVALID_HOSTING_LINK_DOMAIN"
664671
case .webInternalError:
665672
return "ERROR_WEB_INTERNAL_ERROR"
666673
case .webSignInUserInteractionFailure:
@@ -905,6 +912,9 @@ private let kErrorInvalidProviderID =
905912
private let kErrorInvalidDynamicLinkDomain =
906913
"The Firebase Dynamic Link domain used is either not configured or is unauthorized for the current project."
907914

915+
private let kErrorInvalidHostingLinkDomain =
916+
"The provided hosting link domain is not configured in Firebase Hosting or is not owned by the current project."
917+
908918
private let kErrorInternalError =
909919
"An internal error has occurred, print and inspect the error details for more information."
910920

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ enum AuthMenu: String {
4343
case deleteApp
4444
case actionType
4545
case continueURL
46+
case linkDomain
4647
case requestVerifyEmail
4748
case requestPasswordReset
4849
case resetPassword
@@ -117,6 +118,8 @@ enum AuthMenu: String {
117118
return "Action Type"
118119
case .continueURL:
119120
return "Continue URL"
121+
case .linkDomain:
122+
return "Link Domain"
120123
case .requestVerifyEmail:
121124
return "Request Verify Email"
122125
case .requestPasswordReset:
@@ -197,6 +200,8 @@ enum AuthMenu: String {
197200
self = .actionType
198201
case "Continue URL":
199202
self = .continueURL
203+
case "Link Domain":
204+
self = .linkDomain
200205
case "Request Verify Email":
201206
self = .requestVerifyEmail
202207
case "Request Password Reset":
@@ -328,6 +333,7 @@ class AuthMenuData: DataSourceProvidable {
328333
let items: [Item] = [
329334
Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name),
330335
Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true),
336+
Item(title: AuthMenu.linkDomain.name, detailTitle: "--", isEditable: true),
331337
Item(title: AuthMenu.requestVerifyEmail.name),
332338
Item(title: AuthMenu.requestPasswordReset.name),
333339
Item(title: AuthMenu.resetPassword.name),

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AccountLinkingViewController.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ class AccountLinkingViewController: UIViewController, DataSourceProviderDelegate
354354
/// Similar to in `PasswordlessViewController`, enter the authorized domain.
355355
/// Please refer to this Quickstart's README for more information.
356356
private let authorizedDomain: String = "ENTER AUTHORIZED DOMAIN"
357+
358+
/// This is the replacement for customized dynamic link domain.
359+
private let customDomain: String = "ENTER AUTHORIZED HOSTING DOMAIN"
357360
/// Maintain a reference to the email entered for linking user to Passwordless.
358361
private var email: String?
359362

@@ -380,6 +383,7 @@ class AccountLinkingViewController: UIViewController, DataSourceProviderDelegate
380383
// The sign-in operation must be completed in the app.
381384
actionCodeSettings.handleCodeInApp = true
382385
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
386+
actionCodeSettings.linkDomain = customDomain
383387

384388
AppManager.shared.auth()
385389
.sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
3939
var authStateDidChangeListeners: [AuthStateDidChangeListenerHandle] = []
4040
var IDTokenDidChangeListeners: [IDTokenDidChangeListenerHandle] = []
4141
var actionCodeContinueURL: URL?
42+
var actionCodeLinkDomain: String?
4243
var actionCodeRequestType: ActionCodeRequestType = .inApp
4344

4445
let spinner = UIActivityIndicatorView(style: .medium)
@@ -69,6 +70,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
6970
let settings = ActionCodeSettings()
7071
settings.url = actionCodeContinueURL
7172
settings.handleCodeInApp = (actionCodeRequestType == .inApp)
73+
settings.linkDomain = actionCodeLinkDomain
7274
return settings
7375
}
7476

@@ -156,6 +158,9 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
156158
case .continueURL:
157159
changeActionCodeContinueURL(at: indexPath)
158160

161+
case .linkDomain:
162+
changeActionCodeLinkDomain(at: indexPath)
163+
159164
case .requestVerifyEmail:
160165
requestVerifyEmail()
161166

@@ -552,7 +557,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
552557
private func changeActionCodeContinueURL(at indexPath: IndexPath) {
553558
showTextInputPrompt(with: "Continue URL:", completion: { newContinueURL in
554559
self.actionCodeContinueURL = URL(string: newContinueURL)
555-
print("Successfully set Continue URL to: \(newContinueURL)")
560+
print("Successfully set Continue URL to: \(newContinueURL)")
556561
self.dataSourceProvider.updateItem(
557562
at: indexPath,
558563
item: Item(
@@ -565,6 +570,22 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
565570
})
566571
}
567572

573+
private func changeActionCodeLinkDomain(at indexPath: IndexPath) {
574+
showTextInputPrompt(with: "Link Domain:", completion: { newLinkDomain in
575+
self.actionCodeLinkDomain = newLinkDomain
576+
print("Successfully set Link Domain to: \(newLinkDomain)")
577+
self.dataSourceProvider.updateItem(
578+
at: indexPath,
579+
item: Item(
580+
title: AuthMenu.linkDomain.name,
581+
detailTitle: self.actionCodeLinkDomain,
582+
isEditable: true
583+
)
584+
)
585+
self.tableView.reloadData()
586+
})
587+
}
588+
568589
private func requestVerifyEmail() {
569590
showSpinner()
570591
let completionHandler: ((any Error)?) -> Void = { [weak self] error in

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PasswordlessViewController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class PasswordlessViewController: OtherAuthViewController {
3232
// MARK: - Firebase 🔥
3333

3434
private let authorizedDomain: String = "ENTER AUTHORIZED DOMAIN"
35+
private let customDomain: String = "ENTER AUTHORIZED HOSTING DOMAIN"
3536

3637
private func sendSignInLink(to email: String) {
3738
let actionCodeSettings = ActionCodeSettings()
@@ -42,6 +43,7 @@ class PasswordlessViewController: OtherAuthViewController {
4243
// The sign-in operation must be completed in the app.
4344
actionCodeSettings.handleCodeInApp = true
4445
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
46+
actionCodeSettings.linkDomain = customDomain
4547

4648
AppManager.shared.auth()
4749
.sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in

FirebaseAuth/Tests/Unit/GetOOBConfirmationCodeTests.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
3434
private let kAndroidMinimumVersionKey = "androidMinimumVersion"
3535
private let kCanHandleCodeInAppKey = "canHandleCodeInApp"
3636
private let kDynamicLinkDomainKey = "dynamicLinkDomain"
37+
private let kLinkDomainKey = "linkDomain"
3738
private let kExpectedAPIURL =
3839
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/getOobConfirmationCode?key=APIKey"
3940
private let kOOBCodeKey = "oobCode"
@@ -66,6 +67,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
6667
XCTAssertEqual(decodedRequest[kAndroidInstallAppKey] as? Bool, true)
6768
XCTAssertEqual(decodedRequest[kCanHandleCodeInAppKey] as? Bool, true)
6869
XCTAssertEqual(decodedRequest[kDynamicLinkDomainKey] as? String, kDynamicLinkDomain)
70+
XCTAssertEqual(decodedRequest[kLinkDomainKey] as? String, kLinkDomain)
6971
}
7072
}
7173

@@ -110,6 +112,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
110112
XCTAssertEqual(decodedRequest[kAndroidInstallAppKey] as? Bool, true)
111113
XCTAssertEqual(decodedRequest[kCanHandleCodeInAppKey] as? Bool, true)
112114
XCTAssertEqual(decodedRequest[kDynamicLinkDomainKey] as? String, kDynamicLinkDomain)
115+
XCTAssertEqual(decodedRequest[kLinkDomainKey] as? String, kLinkDomain)
113116
XCTAssertEqual(decodedRequest[kCaptchaResponseKey] as? String, kTestCaptchaResponse)
114117
XCTAssertEqual(decodedRequest[kClientTypeKey] as? String, kTestClientType)
115118
XCTAssertEqual(decodedRequest[kRecaptchaVersionKey] as? String, kTestRecaptchaVersion)

0 commit comments

Comments
 (0)