Skip to content

sample app UI #15184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
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 @@ -53,6 +53,8 @@ enum AuthMenu: String {
case phoneEnroll
case totpEnroll
case multifactorUnenroll
case passkeySignUp
case passkeyEnroll

// More intuitively named getter for `rawValue`.
var id: String { rawValue }
Expand Down Expand Up @@ -139,6 +141,11 @@ enum AuthMenu: String {
return "TOTP Enroll"
case .multifactorUnenroll:
return "Multifactor unenroll"
// Passkey
case .passkeySignUp:
return "Sign Up with Passkey"
case .passkeyEnroll:
return "Enroll with Passkey"
}
}

Expand Down Expand Up @@ -220,6 +227,10 @@ enum AuthMenu: String {
self = .totpEnroll
case "Multifactor unenroll":
self = .multifactorUnenroll
case "Sign Up with Passkey":
self = .passkeySignUp
case "Enroll with Passkey":
self = .passkeyEnroll
default:
return nil
}
Expand Down Expand Up @@ -354,9 +365,18 @@ class AuthMenuData: DataSourceProvidable {
return Section(headerDescription: header, items: items)
}

static var passkeySection: Section {
let header = "Passkey"
let items: [Item] = [
Item(title: AuthMenu.passkeySignUp.name),
Item(title: AuthMenu.passkeyEnroll.name),
]
return Section(headerDescription: header, items: items)
}

static let sections: [Section] =
[settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
customAuthDomainSection, appSection, oobSection, multifactorSection]
customAuthDomainSection, appSection, oobSection, multifactorSection, passkeySection]

static var authLinkSections: [Section] {
let allItems = [providerSection, emailPasswordSection, otherSection].flatMap { $0.items }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {

case .multifactorUnenroll:
mfaUnenroll()

case .passkeySignUp:
passkeySignUp()

case .passkeyEnroll:
Task { await passkeyEnroll() }
}
}

Expand Down Expand Up @@ -922,6 +928,56 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
}
}

// MARK: - Passkey

private func passkeySignUp() {
guard #available(iOS 16.0, macOS 12.0, tvOS 16.0, *) else {
print("OS version is not supported for this action.")
return
}
Task {
do {
_ = try await AppManager.shared.auth().signInAnonymously()
print("sign-in anonymously succeeded.")
if let uid = AppManager.shared.auth().currentUser?.uid {
print("User ID: \(uid)")
}
// Continue to enroll a passkey.
await passkeyEnroll()
} catch {
print("sign-in anonymously failed: \(error.localizedDescription)")
self.showAlert(for: "Anonymous Sign-In Failed")
}
}
}

private func passkeyEnroll() async {
guard let user = AppManager.shared.auth().currentUser else {
showAlert(for: "Please sign in first.")
return
}
guard let passkeyName = await showTextInputPrompt(with: "Passkey name") else {
print("Passkey enrollment cancelled: no name entered.")
return
}
guard #available(iOS 16.0, macOS 12.0, tvOS 16.0, *) else {
showAlert(for: "Not Supported", message: "This OS version does not support passkeys.")
return
}

do {
let request = try await user.startPasskeyEnrollment(withName: passkeyName)
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
print("Started passkey enrollment (challenge created).")
} catch {
showAlert(for: "Passkey enrollment failed", message: error.localizedDescription)
print("startPasskeyEnrollment failed: \(error.localizedDescription)")
}
}

// MARK: - Private Helpers

private func showTextInputPrompt(with message: String, completion: ((String) -> Void)? = nil) {
Expand Down Expand Up @@ -1019,14 +1075,35 @@ extension AuthViewController: LoginDelegate {
}
}

// MARK: - Implementing Sign in with Apple with Firebase
// MARK: - Implementing Passkeys and Sign in with Apple with Firebase

extension AuthViewController: ASAuthorizationControllerDelegate,
ASAuthorizationControllerPresentationContextProviding {
// MARK: ASAuthorizationControllerDelegate

func authorizationController(controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization) {
if #available(iOS 16.0, macOS 12.0, tvOS 16.0, *),
let regCred = authorization.credential
as? ASAuthorizationPlatformPublicKeyCredentialRegistration {
Task { @MainActor [weak self] in
guard let self else { return }
do {
guard let user = AppManager.shared.auth().currentUser else {
self.showAlert(for: "Finalize failed", message: "No signed-in user.")
return
}
_ = try await user.finalizePasskeyEnrollment(withPlatformCredential: regCred)
self.showAlert(for: "Passkey Enrollment", message: "Succeeded")
print("Passkey Enrollment succeeded.")
} catch {
self.showAlert(for: "Passkey Enrollment failed", message: error.localizedDescription)
print("Finalize enrollment failed: \(error.localizedDescription)")
}
}
return
}

guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential
else {
print("Unable to retrieve AppleIDCredential")
Expand Down Expand Up @@ -1074,10 +1151,10 @@ extension AuthViewController: ASAuthorizationControllerDelegate,

func authorizationController(controller: ASAuthorizationController,
didCompleteWithError error: any Error) {
// Ensure that you have:
print("ASAuthorization failed: \(error)")
// for Sign In with Apple, ensure that you have:
// - enabled `Sign in with Apple` on the Firebase console
// - added the `Sign in with Apple` capability for this project
print("Sign in with Apple failed: \(error)")
}

// MARK: ASAuthorizationControllerPresentationContextProviding
Expand Down
Loading