Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ import Foundation
/// The Firebase Dynamic Link domain used for out of band code flow.
@objc open var dynamicLinkDomain: String?

/// The out of band custom domain for handling code in app.
@objc open var linkDomain: String?

/// Sets the iOS bundle ID.
@objc override public init() {
iOSBundleID = Bundle.main.bundleIdentifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ private let kCanHandleCodeInAppKey = "canHandleCodeInApp"
/// The key for the "dynamic link domain" value in the request.
private let kDynamicLinkDomainKey = "dynamicLinkDomain"

/// The key for the "link domain" value in the request.
private let kLinkDomainKey = "linkDomain"

/// The value for the "PASSWORD_RESET" request type.
private let kPasswordResetRequestTypeValue = "PASSWORD_RESET"

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

/// The Firebase Hosting domain used for out of band code flow.
private(set) var linkDomain: String?

/// Response to the captcha.
var captchaResponse: String?

Expand Down Expand Up @@ -172,6 +178,7 @@ class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
androidInstallApp = actionCodeSettings?.androidInstallIfNotAvailable ?? false
handleCodeInApp = actionCodeSettings?.handleCodeInApp ?? false
dynamicLinkDomain = actionCodeSettings?.dynamicLinkDomain
linkDomain = actionCodeSettings?.linkDomain

super.init(
endpoint: kGetOobConfirmationCodeEndpoint,
Expand Down Expand Up @@ -274,6 +281,9 @@ class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
if let dynamicLinkDomain {
body[kDynamicLinkDomainKey] = dynamicLinkDomain
}
if let linkDomain {
body[kLinkDomainKey] = linkDomain
}
if let captchaResponse {
body[kCaptchaResponseKey] = captchaResponse
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum AuthMenu: String {
case deleteApp
case actionType
case continueURL
case linkDomain
case requestVerifyEmail
case requestPasswordReset
case resetPassword
Expand Down Expand Up @@ -117,6 +118,8 @@ enum AuthMenu: String {
return "Action Type"
case .continueURL:
return "Continue URL"
case .linkDomain:
return "Link Domain"
case .requestVerifyEmail:
return "Request Verify Email"
case .requestPasswordReset:
Expand Down Expand Up @@ -197,6 +200,8 @@ enum AuthMenu: String {
self = .actionType
case "Continue URL":
self = .continueURL
case "Link Domain":
self = .linkDomain
case "Request Verify Email":
self = .requestVerifyEmail
case "Request Password Reset":
Expand Down Expand Up @@ -328,6 +333,7 @@ class AuthMenuData: DataSourceProvidable {
let items: [Item] = [
Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name),
Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true),
Item(title: AuthMenu.linkDomain.name, detailTitle: "--", isEditable: true),
Item(title: AuthMenu.requestVerifyEmail.name),
Item(title: AuthMenu.requestPasswordReset.name),
Item(title: AuthMenu.resetPassword.name),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ class AccountLinkingViewController: UIViewController, DataSourceProviderDelegate
/// Similar to in `PasswordlessViewController`, enter the authorized domain.
/// Please refer to this Quickstart's README for more information.
private let authorizedDomain: String = "ENTER AUTHORIZED DOMAIN"

/// This is the replacement for customized dynamic link domain.
private let customDomain: String = "ENTER AUTHORIZED HOSTING DOMAIN"
/// Maintain a reference to the email entered for linking user to Passwordless.
private var email: String?

Expand All @@ -343,6 +346,7 @@ class AccountLinkingViewController: UIViewController, DataSourceProviderDelegate
// The sign-in operation must be completed in the app.
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
actionCodeSettings.linkDomain = customDomain

AppManager.shared.auth()
.sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
var authStateDidChangeListeners: [AuthStateDidChangeListenerHandle] = []
var IDTokenDidChangeListeners: [IDTokenDidChangeListenerHandle] = []
var actionCodeContinueURL: URL?
var actionCodeLinkDomain: String?
var actionCodeRequestType: ActionCodeRequestType = .inApp

let spinner = UIActivityIndicatorView(style: .medium)
Expand Down Expand Up @@ -69,6 +70,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
let settings = ActionCodeSettings()
settings.url = actionCodeContinueURL
settings.handleCodeInApp = (actionCodeRequestType == .inApp)
settings.linkDomain = actionCodeLinkDomain
return settings
}

Expand Down Expand Up @@ -156,6 +158,9 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
case .continueURL:
changeActionCodeContinueURL(at: indexPath)

case .linkDomain:
changeActionCodeLinkDomain(at: indexPath)

case .requestVerifyEmail:
requestVerifyEmail()

Expand Down Expand Up @@ -552,7 +557,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
private func changeActionCodeContinueURL(at indexPath: IndexPath) {
showTextInputPrompt(with: "Continue URL:", completion: { newContinueURL in
self.actionCodeContinueURL = URL(string: newContinueURL)
print("Successfully set Continue URL to: \(newContinueURL)")
print("Successfully set Continue URL to: \(newContinueURL)")
self.dataSourceProvider.updateItem(
at: indexPath,
item: Item(
Expand All @@ -565,6 +570,22 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
})
}

private func changeActionCodeLinkDomain(at indexPath: IndexPath) {
showTextInputPrompt(with: "Link Domain:", completion: { newLinkDomain in
self.actionCodeLinkDomain = newLinkDomain
print("Successfully set Link Domain to: \(newLinkDomain)")
self.dataSourceProvider.updateItem(
at: indexPath,
item: Item(
title: AuthMenu.linkDomain.name,
detailTitle: self.actionCodeLinkDomain,
isEditable: true
)
)
self.tableView.reloadData()
})
}

private func requestVerifyEmail() {
showSpinner()
let completionHandler: ((any Error)?) -> Void = { [weak self] error in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class PasswordlessViewController: OtherAuthViewController {
// MARK: - Firebase 🔥

private let authorizedDomain: String = "ENTER AUTHORIZED DOMAIN"
private let customDomain: String = "ENTER AUTHORIZED HOSTING DOMAIN"

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

AppManager.shared.auth()
.sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,35 @@ class AuthenticationExampleUITests: XCTestCase {
removeUIInterruptionMonitor(interruptionMonitor)
}

func testEmailLinkSentSuccessfully() {
app.staticTexts["Email Link/Passwordless"].tap()

let testEmail = "[email protected]"
app.textFields["Enter Authentication Email"].tap()
app.textFields["Enter Authentication Email"].typeText(testEmail)
app.buttons["return"].tap() // Dismiss keyboard
app.buttons["Send Sign In Link"].tap()

// Wait for the error message to appear (if there is an error)
let errorAlert = app.alerts.staticTexts["Error"]
let errorExists = errorAlert.waitForExistence(timeout: 5.0)

app.swipeDown(velocity: .fast)

// Assert that there is no error message (success case)
// The email sign in link is sent successfully if no error message appears
XCTAssertFalse(errorExists, "Error")

// Go back and check that there is no user that is signed in
app.tabBars.firstMatch.buttons.element(boundBy: 1).tap()
wait(forElement: app.navigationBars["User"], timeout: 5.0)
XCTAssertEqual(
app.cells.count,
0,
"The user shouldn't be signed in and the user view should have no cells."
)
}

// MARK: - Private Helpers

private func signOut() {
Expand Down
3 changes: 3 additions & 0 deletions FirebaseAuth/Tests/Unit/GetOOBConfirmationCodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
private let kAndroidMinimumVersionKey = "androidMinimumVersion"
private let kCanHandleCodeInAppKey = "canHandleCodeInApp"
private let kDynamicLinkDomainKey = "dynamicLinkDomain"
private let kLinkDomainKey = "linkDomain"
private let kExpectedAPIURL =
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/getOobConfirmationCode?key=APIKey"
private let kOOBCodeKey = "oobCode"
Expand Down Expand Up @@ -66,6 +67,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
XCTAssertEqual(decodedRequest[kAndroidInstallAppKey] as? Bool, true)
XCTAssertEqual(decodedRequest[kCanHandleCodeInAppKey] as? Bool, true)
XCTAssertEqual(decodedRequest[kDynamicLinkDomainKey] as? String, kDynamicLinkDomain)
XCTAssertEqual(decodedRequest[kLinkDomainKey] as? String, kLinkDomain)
}
}

Expand Down Expand Up @@ -110,6 +112,7 @@ class GetOOBConfirmationCodeTests: RPCBaseTests {
XCTAssertEqual(decodedRequest[kAndroidInstallAppKey] as? Bool, true)
XCTAssertEqual(decodedRequest[kCanHandleCodeInAppKey] as? Bool, true)
XCTAssertEqual(decodedRequest[kDynamicLinkDomainKey] as? String, kDynamicLinkDomain)
XCTAssertEqual(decodedRequest[kLinkDomainKey] as? String, kLinkDomain)
XCTAssertEqual(decodedRequest[kCaptchaResponseKey] as? String, kTestCaptchaResponse)
XCTAssertEqual(decodedRequest[kClientTypeKey] as? String, kTestClientType)
XCTAssertEqual(decodedRequest[kRecaptchaVersionKey] as? String, kTestRecaptchaVersion)
Expand Down
2 changes: 2 additions & 0 deletions FirebaseAuth/Tests/Unit/RPCBaseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class RPCBaseTests: XCTestCase {
let kAndroidPackageName = "androidpackagename"
let kAndroidMinimumVersion = "3.0"
let kDynamicLinkDomain = "test.page.link"
let kLinkDomain = "link.firebaseapp.com"
let kTestPhotoURL = "https://host.domain/image"
let kCreationDateTimeIntervalInSeconds = 1_505_858_500.0
let kLastSignInDateTimeIntervalInSeconds = 1_505_858_583.0
Expand Down Expand Up @@ -304,6 +305,7 @@ class RPCBaseTests: XCTestCase {
settings.handleCodeInApp = true
settings.url = URL(string: kContinueURL)
settings.dynamicLinkDomain = kDynamicLinkDomain
settings.linkDomain = kLinkDomain
return settings
}

Expand Down
Loading