@@ -30,6 +30,13 @@ public enum AuthenticationFlow {
3030 case signUp
3131}
3232
33+ public enum AuthServiceError : Error {
34+ case invalidEmailLink( String )
35+ case notConfiguredProvider( String )
36+ case clientIdNotFound( String )
37+ case notConfiguredActionCodeSettings( String )
38+ }
39+
3340@MainActor
3441final class AuthListenerManager {
3542 private var authStateHandle : AuthStateDidChangeListenerHandle ?
@@ -59,6 +66,7 @@ final class AuthListenerManager {
5966@MainActor
6067@Observable
6168public final class AuthService {
69+ @ObservationIgnored @AppStorage ( " email-link " ) public var emailLink : String ?
6270 public let configuration : AuthConfiguration
6371 public let auth : Auth
6472 private var listenerManager : AuthListenerManager ?
@@ -84,13 +92,8 @@ public final class AuthService {
8492 private var safeGoogleProvider : GoogleProviderProtocol {
8593 get throws {
8694 guard let provider = googleProvider else {
87- throw NSError (
88- domain: " AuthEnvironmentErrorDomain " ,
89- code: 1 ,
90- userInfo: [
91- NSLocalizedDescriptionKey: " `GoogleProviderSwift` has not been configured " ,
92- ]
93- )
95+ throw AuthServiceError
96+ . notConfiguredProvider ( " `GoogleProviderSwift` has not been configured " )
9497 }
9598 return provider
9699 }
@@ -99,18 +102,25 @@ public final class AuthService {
99102 private var safeFacebookProvider : FacebookProviderProtocol {
100103 get throws {
101104 guard let provider = facebookProvider else {
102- throw NSError (
103- domain: " AuthEnvironmentErrorDomain " ,
104- code: 1 ,
105- userInfo: [
106- NSLocalizedDescriptionKey: " `FacebookProviderSwift` has not been configured " ,
107- ]
108- )
105+ throw AuthServiceError
106+ . notConfiguredProvider ( " `FacebookProviderSwift` has not been configured " )
109107 }
110108 return provider
111109 }
112110 }
113111
112+ private func safeActionCodeSettings( emailLinkSignIn: Bool = true ) throws -> ActionCodeSettings {
113+ guard let actionCodeSettings = emailLinkSignIn ? configuration
114+ . emailLinkSignInActionCodeSettings : configuration. verifyEmailActionCodeSettings else {
115+ let errorMessage = emailLinkSignIn ?
116+ " ActionCodeSettings has not been configured for `AuthConfiguration.emailLinkSignInActionCodeSettings` " :
117+ " ActionCodeSettings has not been configured for `AuthConfiguration.verifyEmailActionCodeSettings` "
118+ throw AuthServiceError
119+ . notConfiguredActionCodeSettings ( errorMessage)
120+ }
121+ return actionCodeSettings
122+ }
123+
114124 func updateAuthenticationState( ) {
115125 authenticationState =
116126 ( currentUser == nil || currentUser? . isAnonymous == true )
@@ -126,13 +136,12 @@ public final class AuthService {
126136 public func signInWithGoogle( ) async throws {
127137 authenticationState = . authenticating
128138 do {
129- guard let clientID = auth. app? . options. clientID else { throw NSError (
130- domain: " AuthServiceErrorDomain " ,
131- code: 2 ,
132- userInfo: [
133- NSLocalizedDescriptionKey: " OAuth client ID not found. Please make sure Google Sign-In is enabled in the Firebase console. You may have to download a new GoogleService-Info.plist file after enabling Google Sign-In. " ,
134- ]
135- ) }
139+ guard let clientID = auth. app? . options. clientID else {
140+ throw AuthServiceError
141+ . clientIdNotFound (
142+ " OAuth client ID not found. Please make sure Google Sign-In is enabled in the Firebase console. You may have to download a new GoogleService-Info.plist file after enabling Google Sign-In. "
143+ )
144+ }
136145 let credential = try await safeGoogleProvider. signInWithGoogle ( clientID: clientID)
137146
138147 try await signIn ( with: credential)
@@ -191,9 +200,7 @@ public final class AuthService {
191200
192201 func sendEmailSignInLink( to email: String ) async throws {
193202 do {
194- // TODO: - how does user set action code settings? Needs configuring
195- let actionCodeSettings = ActionCodeSettings ( )
196- actionCodeSettings. handleCodeInApp = true
203+ let actionCodeSettings = try safeActionCodeSettings ( )
197204 try await auth. sendSignInLink (
198205 toEmail: email,
199206 actionCodeSettings: actionCodeSettings
@@ -202,4 +209,22 @@ public final class AuthService {
202209 throw error
203210 }
204211 }
212+
213+ func handleSignInLink( url url: URL ) async throws {
214+ do {
215+ guard let email = emailLink else {
216+ throw AuthServiceError . invalidEmailLink (
217+ " Invalid email address. Most likely, the link you used has expired. Try signing in again. "
218+ )
219+ }
220+ let link = url. absoluteString
221+ if auth. isSignIn ( withEmailLink: link) {
222+ let result = try await auth. signIn ( withEmail: email, link: link)
223+ updateAuthenticationState ( )
224+ emailLink = nil
225+ }
226+ } catch {
227+ throw error
228+ }
229+ }
205230}
0 commit comments