Skip to content

Commit e749e11

Browse files
refactor: ensure authenticate() method is for OAuth only
1 parent 2819e30 commit e749e11

File tree

5 files changed

+90
-58
lines changed

5 files changed

+90
-58
lines changed

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,61 @@
1515
import FirebaseAuth
1616
import SwiftUI
1717

18-
/// Context information for reauthentication
19-
public struct ReauthContext: Equatable {
18+
/// Context information for OAuth provider reauthentication (Google, Apple, Facebook, Twitter, etc.)
19+
public struct OAuthReauthContext: Equatable {
2020
public let providerId: String
2121
public let providerName: String
22-
public let phoneNumber: String?
23-
public let email: String?
2422

25-
public init(providerId: String, providerName: String, phoneNumber: String?, email: String?) {
23+
public init(providerId: String, providerName: String) {
2624
self.providerId = providerId
2725
self.providerName = providerName
28-
self.phoneNumber = phoneNumber
26+
}
27+
28+
public var displayMessage: String {
29+
"Please sign in with \(providerName) to continue"
30+
}
31+
}
32+
33+
/// Context information for email/password reauthentication
34+
public struct EmailReauthContext: Equatable {
35+
public let email: String
36+
37+
public init(email: String) {
2938
self.email = email
3039
}
3140

3241
public var displayMessage: String {
33-
switch providerId {
34-
case EmailAuthProviderID:
35-
return "Please enter your password to continue"
36-
case PhoneAuthProviderID:
37-
return "Please verify your phone number to continue"
38-
default:
39-
return "Please sign in with \(providerName) to continue"
42+
"Please enter your password to continue"
43+
}
44+
}
45+
46+
/// Context information for phone number reauthentication
47+
public struct PhoneReauthContext: Equatable {
48+
public let phoneNumber: String
49+
50+
public init(phoneNumber: String) {
51+
self.phoneNumber = phoneNumber
52+
}
53+
54+
public var displayMessage: String {
55+
"Please verify your phone number to continue"
56+
}
57+
}
58+
59+
/// Type-safe wrapper for reauthentication contexts
60+
public enum ReauthenticationType: Equatable {
61+
case oauth(OAuthReauthContext)
62+
case email(EmailReauthContext)
63+
case phone(PhoneReauthContext)
64+
65+
public var displayMessage: String {
66+
switch self {
67+
case let .oauth(context):
68+
return context.displayMessage
69+
case let .email(context):
70+
return context.displayMessage
71+
case let .phone(context):
72+
return context.displayMessage
4073
}
4174
}
4275
}
@@ -99,15 +132,15 @@ public enum AuthServiceError: LocalizedError {
99132
case clientIdNotFound(String)
100133
case notConfiguredActionCodeSettings(String)
101134

102-
/// Simple reauthentication required (Google, Apple, Facebook, Twitter, etc.)
135+
/// OAuth reauthentication required (Google, Apple, Facebook, Twitter, etc.)
103136
/// Can be passed directly to `reauthenticate(context:)` method
104-
case simpleReauthenticationRequired(context: ReauthContext)
137+
case oauthReauthenticationRequired(context: OAuthReauthContext)
105138

106139
/// Email reauthentication required - user must handle password prompt externally
107-
case emailReauthenticationRequired(context: ReauthContext)
140+
case emailReauthenticationRequired(context: EmailReauthContext)
108141

109142
/// Phone reauthentication required - user must handle SMS verification flow externally
110-
case phoneReauthenticationRequired(context: ReauthContext)
143+
case phoneReauthenticationRequired(context: PhoneReauthContext)
111144

112145
case invalidCredentials(String)
113146
case signInFailed(underlying: Error)
@@ -128,11 +161,11 @@ public enum AuthServiceError: LocalizedError {
128161
return description
129162
case let .notConfiguredActionCodeSettings(description):
130163
return description
131-
case let .simpleReauthenticationRequired(context):
164+
case let .oauthReauthenticationRequired(context):
132165
return "Please sign in again with \(context.providerName) to continue"
133-
case let .emailReauthenticationRequired(context):
166+
case .emailReauthenticationRequired:
134167
return "Please enter your password to continue"
135-
case let .phoneReauthenticationRequired(context):
168+
case .phoneReauthenticationRequired:
136169
return "Please verify your phone number to continue"
137170
case let .invalidCredentials(description):
138171
return description
@@ -155,3 +188,4 @@ public enum AuthServiceError: LocalizedError {
155188
}
156189
}
157190
}
191+

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -719,12 +719,12 @@ public extension AuthService {
719719
}
720720
}
721721

722-
/// Reauthenticates with a simple provider (Google, Apple, Facebook, Twitter, etc.)
723-
/// - Parameter context: The reauth context from `simpleReauthenticationRequired` error
722+
/// Reauthenticates with an OAuth provider (Google, Apple, Facebook, Twitter, etc.)
723+
/// - Parameter context: The reauth context from `oauthReauthenticationRequired` error
724724
/// - Throws: Error if reauthentication fails or provider is not found
725725
/// - Note: This only works for providers that can automatically obtain credentials.
726726
/// For email/phone, handle the flow externally and use `reauthenticate(with:)`
727-
func reauthenticate(context: ReauthContext) async throws {
727+
func reauthenticate(context: OAuthReauthContext) async throws {
728728
guard let user = currentUser else {
729729
throw AuthServiceError.noCurrentUser
730730
}
@@ -779,20 +779,22 @@ public extension AuthService {
779779
providerDisplayName = getProviderDisplayName(providerId)
780780
}
781781

782-
let context = ReauthContext(
783-
providerId: providerId,
784-
providerName: providerDisplayName,
785-
phoneNumber: currentUser?.phoneNumber,
786-
email: currentUser?.email
787-
)
788-
789782
switch providerId {
790783
case EmailAuthProviderID:
784+
guard let email = currentUser?.email else {
785+
throw AuthServiceError.noCurrentUser
786+
}
787+
let context = EmailReauthContext(email: email)
791788
throw AuthServiceError.emailReauthenticationRequired(context: context)
792789
case PhoneAuthProviderID:
790+
guard let phoneNumber = currentUser?.phoneNumber else {
791+
throw AuthServiceError.noCurrentUser
792+
}
793+
let context = PhoneReauthContext(phoneNumber: phoneNumber)
793794
throw AuthServiceError.phoneReauthenticationRequired(context: context)
794795
default:
795-
throw AuthServiceError.simpleReauthenticationRequired(context: context)
796+
let context = OAuthReauthContext(providerId: providerId, providerName: providerDisplayName)
797+
throw AuthServiceError.oauthReauthenticationRequired(context: context)
796798
}
797799
}
798800

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/ReauthenticationCoordinator.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import Observation
2020
@Observable
2121
public final class ReauthenticationCoordinator {
2222
public var isReauthenticating = false
23-
public var reauthContext: ReauthContext?
23+
public var reauthContext: ReauthenticationType?
2424
public var showingPhoneReauth = false
2525
public var showingPhoneReauthAlert = false
2626
public var showingEmailPasswordPrompt = false
@@ -30,19 +30,19 @@ public final class ReauthenticationCoordinator {
3030
public init() {}
3131

3232
/// Request reauthentication from the user
33-
public func requestReauth(context: ReauthContext) async throws {
33+
public func requestReauth(context: ReauthenticationType) async throws {
3434
return try await withCheckedThrowingContinuation { continuation in
3535
self.continuation = continuation
3636
self.reauthContext = context
3737

38-
// Route to appropriate flow based on provider
39-
switch context.providerId {
40-
case PhoneAuthProviderID:
38+
// Route to appropriate flow based on context type
39+
switch context {
40+
case .phone:
4141
self.showingPhoneReauthAlert = true
42-
case EmailAuthProviderID:
42+
case .email:
4343
self.showingEmailPasswordPrompt = true
44-
default:
45-
// For simple providers (Google, Apple, etc.)
44+
case .oauth:
45+
// For OAuth providers (Google, Apple, etc.)
4646
self.isReauthenticating = true
4747
}
4848
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/ReauthenticationHelpers.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,21 @@ public func withReauthenticationIfNeeded(authService _: AuthService,
2929
do {
3030
try await operation()
3131
} catch let error as AuthServiceError {
32-
// Check if this is a reauthentication error
33-
let context: ReauthContext?
32+
// Check if this is a reauthentication error and extract the context
33+
let reauthContext: ReauthenticationType
3434

3535
switch error {
3636
case let .emailReauthenticationRequired(ctx):
37-
context = ctx
37+
reauthContext = .email(ctx)
3838
case let .phoneReauthenticationRequired(ctx):
39-
context = ctx
40-
case let .simpleReauthenticationRequired(ctx):
41-
context = ctx
39+
reauthContext = .phone(ctx)
40+
case let .oauthReauthenticationRequired(ctx):
41+
reauthContext = .oauth(ctx)
4242
default:
4343
// Not a reauth error, rethrow
4444
throw error
4545
}
4646

47-
guard let reauthContext = context else {
48-
throw error
49-
}
50-
5147
// Request reauthentication through coordinator (shows UI)
5248
try await coordinator.requestReauth(context: reauthContext)
5349

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/ReauthenticationModifier.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct ReauthenticationModifier: ViewModifier {
2222

2323
func body(content: Content) -> some View {
2424
content
25-
// Alert for simple providers only (Google, Apple, etc.)
25+
// Alert for OAuth providers only (Google, Apple, etc.)
2626
.alert(
2727
"Authentication Required",
2828
isPresented: $coordinator.isReauthenticating
@@ -50,24 +50,24 @@ struct ReauthenticationModifier: ViewModifier {
5050
coordinator.reauthCancelled()
5151
}
5252
} message: {
53-
if let phoneNumber = coordinator.reauthContext?.phoneNumber {
54-
Text("For security, we need to verify your phone number: \(phoneNumber)")
53+
if case let .phone(context) = coordinator.reauthContext {
54+
Text("For security, we need to verify your phone number: \(context.phoneNumber)")
5555
}
5656
}
5757
// Sheet for phone reauthentication
5858
.sheet(isPresented: $coordinator.showingPhoneReauth) {
59-
if let phoneNumber = coordinator.reauthContext?.phoneNumber {
59+
if case let .phone(context) = coordinator.reauthContext {
6060
PhoneReauthView(
61-
phoneNumber: phoneNumber,
61+
phoneNumber: context.phoneNumber,
6262
coordinator: coordinator
6363
)
6464
}
6565
}
6666
// Sheet for email reauthentication
6767
.sheet(isPresented: $coordinator.showingEmailPasswordPrompt) {
68-
if let email = coordinator.reauthContext?.email {
68+
if case let .email(context) = coordinator.reauthContext {
6969
EmailReauthView(
70-
email: email,
70+
email: context.email,
7171
coordinator: coordinator
7272
)
7373
}
@@ -77,9 +77,9 @@ struct ReauthenticationModifier: ViewModifier {
7777
private func performReauth() {
7878
Task {
7979
do {
80-
guard let context = coordinator.reauthContext else { return }
80+
guard case let .oauth(context) = coordinator.reauthContext else { return }
8181

82-
// For simple providers (Google, Apple, etc.), call reauthenticate with context
82+
// For OAuth providers (Google, Apple, etc.), call reauthenticate with context
8383
try await authService.reauthenticate(context: context)
8484
coordinator.reauthCompleted()
8585
} catch {

0 commit comments

Comments
 (0)