Skip to content

Commit 1ed8dcc

Browse files
feat: Facebook auth reauthentication
1 parent bf922dd commit 1ed8dcc

File tree

7 files changed

+86
-30
lines changed

7 files changed

+86
-30
lines changed

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AccountService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ extension NSError {
1111
}
1212
}
1313

14-
enum AuthenticationToken {
14+
public enum AuthenticationToken {
1515
case apple(ASAuthorizationAppleIDCredential, String)
1616
case firebase(String)
1717
}
1818

19-
protocol AuthenticatedOperation {
19+
public protocol AuthenticatedOperation {
2020
func callAsFunction(on user: User) async throws
2121
func reauthenticate() async throws -> AuthenticationToken
2222
}
2323

24-
extension AuthenticatedOperation {
24+
public extension AuthenticatedOperation {
2525
func callAsFunction(on _: User,
2626
_ performOperation: () async throws -> Void) async throws {
2727
do {

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,28 @@ public final class AuthService {
8282
public var authenticationFlow: AuthenticationFlow = .login
8383
public var errorMessage = ""
8484
public let passwordPrompt: PasswordPromptCoordinator = .init()
85-
86-
public var googleProvider: (any GoogleProviderAuthUIProtocol)?
87-
public var facebookProvider: (any FacebookProviderAuthUIProtocol)?
88-
public var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
85+
private var googleProvider: (any GoogleProviderAuthUIProtocol)?
86+
private var facebookProvider: (any FacebookProviderAuthUIProtocol)?
87+
private var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
8988

9089
private var listenerManager: AuthListenerManager?
9190
private var signedInCredential: AuthCredential?
9291

9392
private var providers: [ExternalAuthProvider] = []
9493
public func register(provider: ExternalAuthProvider) {
95-
providers.append(provider)
94+
switch provider {
95+
case let google as GoogleProviderAuthUIProtocol:
96+
googleProvider = google
97+
providers.append(provider)
98+
case let facebook as FacebookProviderAuthUIProtocol:
99+
facebookProvider = facebook
100+
providers.append(provider)
101+
case let phone as PhoneAuthProviderAuthUIProtocol:
102+
phoneAuthProvider = phone
103+
providers.append(provider)
104+
default:
105+
break
106+
}
96107
}
97108

98109
public func renderButtons(spacing: CGFloat = 16) -> AnyView {
@@ -119,7 +130,7 @@ public final class AuthService {
119130
get throws {
120131
guard let provider = facebookProvider else {
121132
throw AuthServiceError
122-
.notConfiguredProvider("`FacebookProviderSwift` has not been configured")
133+
.notConfiguredProvider("`FacebookProviderAuthUI` has not been configured")
123134
}
124135
return provider
125136
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// AccountService+Facebook.swift
3+
// FirebaseUI
4+
//
5+
// Created by Russell Wheatley on 14/05/2025.
6+
//
7+
8+
@preconcurrency import FirebaseAuth
9+
import FirebaseAuthSwiftUI
10+
import Observation
11+
12+
protocol FacebookOperationReauthentication {}
13+
14+
extension FacebookOperationReauthentication {
15+
func reauthenticate() async throws -> AuthenticationToken {
16+
guard let user = Auth.auth().currentUser else {
17+
throw AuthServiceError.reauthenticationRequired("No user currently signed-in")
18+
}
19+
20+
do {
21+
let credential = try await FacebookProviderAuthUI.shared
22+
.signInWithFacebook(isLimitedLogin: FacebookProviderAuthUI.shared.isLimitedLogin)
23+
try await user.reauthenticate(with: credential)
24+
25+
return .firebase("")
26+
} catch {
27+
throw AuthServiceError.signInFailed(underlying: error)
28+
}
29+
}
30+
}
31+
32+
class FacebookDeleteUserOperation: AuthenticatedOperation, FacebookOperationReauthentication {
33+
init() {}
34+
35+
func callAsFunction(on user: User) async throws {
36+
try await callAsFunction(on: user) {
37+
try await user.delete()
38+
}
39+
}
40+
}

FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AuthService+Facebook.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import FirebaseAuthSwiftUI
1010
public extension AuthService {
1111
@discardableResult
1212
func withFacebookSignIn(scopes scopes: [String]? = nil) -> AuthService {
13-
facebookProvider = FacebookProviderAuthUI(scopes: scopes)
14-
register(provider: facebookProvider!)
13+
FacebookProviderAuthUI.configureSharedInstance(scopes: scopes)
14+
register(provider: FacebookProviderAuthUI.shared)
1515
return self
1616
}
1717
}

FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderAuthUI.swift

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,38 @@ let kFacebookEmailScope = "email"
99
let kFacebookProfileScope = "public_profile"
1010
let kDefaultFacebookScopes = [kFacebookEmailScope, kFacebookProfileScope]
1111

12-
public enum FacebookLoginType {
13-
case classic
14-
case limitedLogin
15-
}
16-
1712
public enum FacebookProviderError: Error {
1813
case signInCancelled(String)
1914
case configurationInvalid(String)
2015
case accessToken(String)
2116
case authenticationToken(String)
2217
}
2318

24-
public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol {
19+
public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol, @unchecked Sendable {
2520
public let id: String = "facebook"
2621
let scopes: [String]
2722
let shortName = "Facebook"
2823
let providerId = "facebook.com"
2924
private let loginManager = LoginManager()
3025
private var rawNonce: String
3126
private var shaNonce: String
27+
// Needed for reauthentication
28+
@MainActor public var isLimitedLogin: Bool = true
29+
30+
@MainActor private static var _shared: FacebookProviderAuthUI?
31+
32+
@MainActor public static var shared: FacebookProviderAuthUI {
33+
guard let instance = _shared else {
34+
fatalError("`FacebookProviderAuthUI` has not been configured")
35+
}
36+
return instance
37+
}
38+
39+
@MainActor public static func configureSharedInstance(scopes: [String]? = nil) {
40+
_shared = FacebookProviderAuthUI(scopes: scopes)
41+
}
3242

33-
public init(scopes: [String]? = nil) {
43+
private init(scopes: [String]? = nil) {
3444
self.scopes = scopes ?? kDefaultFacebookScopes
3545
rawNonce = CommonUtils.randomNonce()
3646
shaNonce = CommonUtils.sha256Hash(of: rawNonce)
@@ -41,21 +51,20 @@ public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol {
4151
}
4252

4353
@MainActor public func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential {
44-
let trackingStatus = ATTrackingManager.trackingAuthorizationStatus
45-
let tracking: LoginTracking = trackingStatus != .authorized ? .limited :
46-
(isLimitedLogin ? .limited : .enabled)
54+
let loginType: LoginTracking = isLimitedLogin ? .limited : .enabled
55+
self.isLimitedLogin = isLimitedLogin
4756

4857
guard let configuration: LoginConfiguration = {
49-
if tracking == .limited {
58+
if loginType == .limited {
5059
return LoginConfiguration(
5160
permissions: scopes,
52-
tracking: tracking,
61+
tracking: loginType,
5362
nonce: shaNonce
5463
)
5564
} else {
5665
return LoginConfiguration(
5766
permissions: scopes,
58-
tracking: tracking
67+
tracking: loginType
5968
)
6069
}
6170
}() else {
@@ -74,10 +83,8 @@ public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol {
7483
case .cancelled:
7584
continuation
7685
.resume(throwing: FacebookProviderError.signInCancelled("User cancelled sign-in"))
77-
// showCanceledAlert = true
7886
case let .failed(error):
7987
continuation.resume(throwing: error)
80-
// errorMessage = authService.string.localizedErrorMessage(for: error)
8188
case .success:
8289
continuation.resume()
8390
}

FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AuthService+Google.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ public extension AuthService {
1111
@discardableResult
1212
func withGoogleSignIn(scopes scopes: [String]? = nil) -> AuthService {
1313
let clientID = auth.app?.options.clientID ?? ""
14-
googleProvider = GoogleProviderAuthUI(scopes: scopes, clientID: clientID)
15-
register(provider: googleProvider!)
14+
register(provider: GoogleProviderAuthUI(scopes: scopes, clientID: clientID))
1615
return self
1716
}
1817
}

FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/AuthService+Phone.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import FirebaseAuthSwiftUI
1010
public extension AuthService {
1111
@discardableResult
1212
func withPhoneSignIn() -> AuthService {
13-
phoneAuthProvider = PhoneAuthProviderAuthUI()
14-
register(provider: phoneAuthProvider!)
13+
register(provider: PhoneAuthProviderAuthUI())
1514
return self
1615
}
1716
}

0 commit comments

Comments
 (0)