@@ -8,9 +8,11 @@ public typealias AnyJSON = _Helpers.AnyJSON
8
8
#endif
9
9
10
10
public actor GoTrueClient {
11
+ /// FetchHandler is a type alias for asynchronous network request handling.
11
12
public typealias FetchHandler =
12
13
@Sendable ( _ request: URLRequest ) async throws -> ( Data , URLResponse )
13
14
15
+ /// Configuration struct represents the client configuration.
14
16
public struct Configuration : Sendable {
15
17
public let url : URL
16
18
public var headers : [ String : String ]
@@ -20,6 +22,17 @@ public actor GoTrueClient {
20
22
public let decoder : JSONDecoder
21
23
public let fetch : FetchHandler
22
24
25
+ /// Initializes a GoTrueClient Configuration with optional parameters.
26
+ ///
27
+ /// - Parameters:
28
+ /// - url: The base URL of the GoTrue server.
29
+ /// - headers: (Optional) Custom headers to be included in requests.
30
+ /// - flowType: (Optional) The authentication flow type. Default is `.implicit`.
31
+ /// - localStorage: (Optional) The storage mechanism for local data. Default is a
32
+ /// KeychainLocalStorage.
33
+ /// - encoder: (Optional) The JSON encoder to use for encoding requests.
34
+ /// - decoder: (Optional) The JSON decoder to use for decoding responses.
35
+ /// - fetch: (Optional) The asynchronous fetch handler for network requests.
23
36
public init (
24
37
url: URL ,
25
38
headers: [ String : String ] = [ : ] ,
@@ -81,6 +94,17 @@ public actor GoTrueClient {
81
94
/// Namespace for accessing multi-factor authentication API.
82
95
public let mfa : GoTrueMFA
83
96
97
+ /// Initializes a GoTrueClient with optional parameters.
98
+ ///
99
+ /// - Parameters:
100
+ /// - url: The base URL of the GoTrue server.
101
+ /// - headers: (Optional) Custom headers to be included in requests.
102
+ /// - flowType: (Optional) The authentication flow type. Default is `.implicit`.
103
+ /// - localStorage: (Optional) The storage mechanism for local data. Default is a
104
+ /// KeychainLocalStorage.
105
+ /// - encoder: (Optional) The JSON encoder to use for encoding requests.
106
+ /// - decoder: (Optional) The JSON decoder to use for decoding responses.
107
+ /// - fetch: (Optional) The asynchronous fetch handler for network requests.
84
108
public init (
85
109
url: URL ,
86
110
headers: [ String : String ] = [ : ] ,
@@ -103,6 +127,10 @@ public actor GoTrueClient {
103
127
)
104
128
}
105
129
130
+ /// Initializes a GoTrueClient with a specific configuration.
131
+ ///
132
+ /// - Parameters:
133
+ /// - configuration: The client configuration.
106
134
public init ( configuration: Configuration ) {
107
135
let api = APIClient ( )
108
136
@@ -144,12 +172,13 @@ public actor GoTrueClient {
144
172
)
145
173
}
146
174
147
- public func onAuthStateChange( ) async -> AsyncStream < AuthChangeEvent > {
175
+ /// Listen for auth state changes.
176
+ ///
177
+ /// An `.initialSession` is always emitted when this method is called.
178
+ public func onAuthStateChange( ) async -> AsyncStream < ( event: AuthChangeEvent , session: Session ? ) > {
148
179
let ( id, stream) = await eventEmitter. attachListener ( )
149
180
150
181
Task { [ id] in
151
- _debug ( " emitInitialSessionTask start " )
152
- defer { _debug ( " emitInitialSessionTask end " ) }
153
182
await emitInitialSession ( forStreamWithID: id)
154
183
}
155
184
@@ -192,25 +221,6 @@ public actor GoTrueClient {
192
221
)
193
222
}
194
223
195
- private func prepareForPKCE( ) -> ( codeChallenge: String ? , codeChallengeMethod: String ? ) {
196
- if configuration. flowType == . pkce {
197
- let codeVerifier = PKCE . generateCodeVerifier ( )
198
-
199
- do {
200
- try codeVerifierStorage. storeCodeVerifier ( codeVerifier)
201
- } catch {
202
- _debug ( " Error storing code verifier: \( error) " )
203
- }
204
-
205
- let codeChallenge = PKCE . generateCodeChallenge ( from: codeVerifier)
206
- let codeChallengeMethod = codeVerifier == codeChallenge ? " plain " : " s256 "
207
-
208
- return ( codeChallenge, codeChallengeMethod)
209
- }
210
-
211
- return ( nil , nil )
212
- }
213
-
214
224
/// Creates a new user.
215
225
/// - Parameters:
216
226
/// - phone: User's phone number with international prefix.
@@ -248,7 +258,7 @@ public actor GoTrueClient {
248
258
249
259
if let session = response. session {
250
260
try await sessionManager. update ( session)
251
- await eventEmitter. emit ( . signedIn)
261
+ await eventEmitter. emit ( . signedIn, session : session )
252
262
}
253
263
254
264
return response
@@ -308,7 +318,7 @@ public actor GoTrueClient {
308
318
309
319
if session. user. emailConfirmedAt != nil || session. user. confirmedAt != nil {
310
320
try await sessionManager. update ( session)
311
- await eventEmitter. emit ( . signedIn)
321
+ await eventEmitter. emit ( . signedIn, session : session )
312
322
}
313
323
314
324
return session
@@ -411,7 +421,7 @@ public actor GoTrueClient {
411
421
try codeVerifierStorage. deleteCodeVerifier ( )
412
422
413
423
try await sessionManager. update ( session)
414
- await eventEmitter. emit ( . signedIn)
424
+ await eventEmitter. emit ( . signedIn, session : session )
415
425
416
426
return session
417
427
} catch {
@@ -524,10 +534,10 @@ public actor GoTrueClient {
524
534
)
525
535
526
536
try await sessionManager. update ( session)
527
- await eventEmitter. emit ( . signedIn)
537
+ await eventEmitter. emit ( . signedIn, session : session )
528
538
529
539
if let type = params. first ( where: { $0. name == " type " } ) ? . value, type == " recovery " {
530
- await eventEmitter. emit ( . passwordRecovery)
540
+ await eventEmitter. emit ( . passwordRecovery, session : session )
531
541
}
532
542
533
543
return session
@@ -571,7 +581,7 @@ public actor GoTrueClient {
571
581
}
572
582
573
583
try await sessionManager. update ( session)
574
- await eventEmitter. emit ( . signedIn)
584
+ await eventEmitter. emit ( . signedIn, session : session )
575
585
return session
576
586
}
577
587
@@ -586,9 +596,9 @@ public actor GoTrueClient {
586
596
)
587
597
)
588
598
await sessionManager. remove ( )
589
- await eventEmitter. emit ( . signedOut)
599
+ await eventEmitter. emit ( . signedOut, session : nil )
590
600
} catch {
591
- await eventEmitter. emit ( . signedOut)
601
+ await eventEmitter. emit ( . signedOut, session : nil )
592
602
throw error
593
603
}
594
604
}
@@ -662,7 +672,7 @@ public actor GoTrueClient {
662
672
663
673
if let session = response. session {
664
674
try await sessionManager. update ( session)
665
- await eventEmitter. emit ( . signedIn)
675
+ await eventEmitter. emit ( . signedIn, session : session )
666
676
}
667
677
668
678
return response
@@ -702,7 +712,7 @@ public actor GoTrueClient {
702
712
) . decoded ( as: User . self, decoder: configuration. decoder)
703
713
session. user = updatedUser
704
714
try await sessionManager. update ( session)
705
- await eventEmitter. emit ( . userUpdated)
715
+ await eventEmitter. emit ( . userUpdated, session : session )
706
716
return updatedUser
707
717
}
708
718
@@ -733,14 +743,24 @@ public actor GoTrueClient {
733
743
)
734
744
}
735
745
746
+ /// Refresh and return a new session, regardless of expiry status.
747
+ /// - Parameter refreshToken: The optional refresh token to use for refreshing the session. If
748
+ /// none is provided then this method tries to load the refresh token from the current session.
749
+ /// - Returns: A new session.
736
750
@discardableResult
737
- public func refreshSession( refreshToken: String ) async throws -> Session {
751
+ public func refreshSession( refreshToken: String ? = nil ) async throws -> Session {
752
+ var credentials = UserCredentials ( refreshToken: refreshToken)
753
+ if credentials. refreshToken == nil {
754
+ credentials. refreshToken = try await sessionManager. session ( shouldValidateExpiration: false )
755
+ . refreshToken
756
+ }
757
+
738
758
let session = try await api. execute (
739
759
. init(
740
760
path: " /token " ,
741
761
method: " POST " ,
742
762
query: [ URLQueryItem ( name: " grant_type " , value: " refresh_token " ) ] ,
743
- body: configuration. encoder. encode ( UserCredentials ( refreshToken : refreshToken ) )
763
+ body: configuration. encoder. encode ( credentials )
744
764
)
745
765
) . decoded ( as: Session . self, decoder: configuration. decoder)
746
766
@@ -749,33 +769,41 @@ public actor GoTrueClient {
749
769
. user. confirmedAt != nil
750
770
{
751
771
try await sessionManager. update ( session)
752
- await eventEmitter. emit ( . tokenRefreshed)
772
+ await eventEmitter. emit ( . tokenRefreshed, session : session )
753
773
}
754
774
755
775
return session
756
776
}
757
777
758
- /// Refresh and return a new session, regardless of expiry status.
759
- @discardableResult
760
- public func refreshSession( ) async throws -> Session {
761
- let refreshToken = try await session. refreshToken
762
- return try await refreshSession ( refreshToken: refreshToken)
763
- }
764
-
765
778
private func emitInitialSession( forStreamWithID id: UUID ) async {
766
- _debug ( " start " )
767
- defer { _debug ( " end " ) }
768
-
769
779
let session = try ? await session
770
- await eventEmitter. emit ( session != nil ? . signedIn : . signedOut , id)
780
+ await eventEmitter. emit ( . initialSession , session , id)
771
781
}
772
782
773
- private func _debug(
774
- _ message: String ,
775
- function: StaticString = #function,
776
- line: UInt = #line
777
- ) {
778
- debugPrint ( " [GoTrueClient] \( function) : \( line) \( message) " )
783
+ private func prepareForPKCE( ) -> ( codeChallenge: String ? , codeChallengeMethod: String ? ) {
784
+ if configuration. flowType == . pkce {
785
+ let codeVerifier = PKCE . generateCodeVerifier ( )
786
+
787
+ do {
788
+ try codeVerifierStorage. storeCodeVerifier ( codeVerifier)
789
+ } catch {
790
+ assertionFailure (
791
+ """
792
+ An error occurred while storing the code verifier,
793
+ PKCE flow may not work as expected.
794
+
795
+ Error: \( error. localizedDescription)
796
+ """
797
+ )
798
+ }
799
+
800
+ let codeChallenge = PKCE . generateCodeChallenge ( from: codeVerifier)
801
+ let codeChallengeMethod = codeVerifier == codeChallenge ? " plain " : " s256 "
802
+
803
+ return ( codeChallenge, codeChallengeMethod)
804
+ }
805
+
806
+ return ( nil , nil )
779
807
}
780
808
781
809
private func isImplicitGrantFlow( url: URL ) -> Bool {
@@ -793,6 +821,7 @@ public actor GoTrueClient {
793
821
}
794
822
795
823
extension GoTrueClient {
824
+ /// Notification posted when an auth state event is triggered.
796
825
public static let didChangeAuthStateNotification = Notification . Name (
797
826
" DID_CHANGE_AUTH_STATE_NOTIFICATION "
798
827
)
0 commit comments