@@ -8,9 +8,11 @@ public typealias AnyJSON = _Helpers.AnyJSON
88#endif
99
1010public actor GoTrueClient {
11+ /// FetchHandler is a type alias for asynchronous network request handling.
1112 public typealias FetchHandler =
1213 @Sendable ( _ request: URLRequest ) async throws -> ( Data , URLResponse )
1314
15+ /// Configuration struct represents the client configuration.
1416 public struct Configuration : Sendable {
1517 public let url : URL
1618 public var headers : [ String : String ]
@@ -20,6 +22,17 @@ public actor GoTrueClient {
2022 public let decoder : JSONDecoder
2123 public let fetch : FetchHandler
2224
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.
2336 public init (
2437 url: URL ,
2538 headers: [ String : String ] = [ : ] ,
@@ -81,6 +94,17 @@ public actor GoTrueClient {
8194 /// Namespace for accessing multi-factor authentication API.
8295 public let mfa : GoTrueMFA
8396
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.
84108 public init (
85109 url: URL ,
86110 headers: [ String : String ] = [ : ] ,
@@ -103,6 +127,10 @@ public actor GoTrueClient {
103127 )
104128 }
105129
130+ /// Initializes a GoTrueClient with a specific configuration.
131+ ///
132+ /// - Parameters:
133+ /// - configuration: The client configuration.
106134 public init ( configuration: Configuration ) {
107135 let api = APIClient ( )
108136
@@ -144,12 +172,13 @@ public actor GoTrueClient {
144172 )
145173 }
146174
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 ? ) > {
148179 let ( id, stream) = await eventEmitter. attachListener ( )
149180
150181 Task { [ id] in
151- _debug ( " emitInitialSessionTask start " )
152- defer { _debug ( " emitInitialSessionTask end " ) }
153182 await emitInitialSession ( forStreamWithID: id)
154183 }
155184
@@ -192,25 +221,6 @@ public actor GoTrueClient {
192221 )
193222 }
194223
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-
214224 /// Creates a new user.
215225 /// - Parameters:
216226 /// - phone: User's phone number with international prefix.
@@ -248,7 +258,7 @@ public actor GoTrueClient {
248258
249259 if let session = response. session {
250260 try await sessionManager. update ( session)
251- await eventEmitter. emit ( . signedIn)
261+ await eventEmitter. emit ( . signedIn, session : session )
252262 }
253263
254264 return response
@@ -308,7 +318,7 @@ public actor GoTrueClient {
308318
309319 if session. user. emailConfirmedAt != nil || session. user. confirmedAt != nil {
310320 try await sessionManager. update ( session)
311- await eventEmitter. emit ( . signedIn)
321+ await eventEmitter. emit ( . signedIn, session : session )
312322 }
313323
314324 return session
@@ -411,7 +421,7 @@ public actor GoTrueClient {
411421 try codeVerifierStorage. deleteCodeVerifier ( )
412422
413423 try await sessionManager. update ( session)
414- await eventEmitter. emit ( . signedIn)
424+ await eventEmitter. emit ( . signedIn, session : session )
415425
416426 return session
417427 } catch {
@@ -524,10 +534,10 @@ public actor GoTrueClient {
524534 )
525535
526536 try await sessionManager. update ( session)
527- await eventEmitter. emit ( . signedIn)
537+ await eventEmitter. emit ( . signedIn, session : session )
528538
529539 if let type = params. first ( where: { $0. name == " type " } ) ? . value, type == " recovery " {
530- await eventEmitter. emit ( . passwordRecovery)
540+ await eventEmitter. emit ( . passwordRecovery, session : session )
531541 }
532542
533543 return session
@@ -571,7 +581,7 @@ public actor GoTrueClient {
571581 }
572582
573583 try await sessionManager. update ( session)
574- await eventEmitter. emit ( . signedIn)
584+ await eventEmitter. emit ( . signedIn, session : session )
575585 return session
576586 }
577587
@@ -586,9 +596,9 @@ public actor GoTrueClient {
586596 )
587597 )
588598 await sessionManager. remove ( )
589- await eventEmitter. emit ( . signedOut)
599+ await eventEmitter. emit ( . signedOut, session : nil )
590600 } catch {
591- await eventEmitter. emit ( . signedOut)
601+ await eventEmitter. emit ( . signedOut, session : nil )
592602 throw error
593603 }
594604 }
@@ -662,7 +672,7 @@ public actor GoTrueClient {
662672
663673 if let session = response. session {
664674 try await sessionManager. update ( session)
665- await eventEmitter. emit ( . signedIn)
675+ await eventEmitter. emit ( . signedIn, session : session )
666676 }
667677
668678 return response
@@ -702,7 +712,7 @@ public actor GoTrueClient {
702712 ) . decoded ( as: User . self, decoder: configuration. decoder)
703713 session. user = updatedUser
704714 try await sessionManager. update ( session)
705- await eventEmitter. emit ( . userUpdated)
715+ await eventEmitter. emit ( . userUpdated, session : session )
706716 return updatedUser
707717 }
708718
@@ -733,14 +743,24 @@ public actor GoTrueClient {
733743 )
734744 }
735745
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.
736750 @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+
738758 let session = try await api. execute (
739759 . init(
740760 path: " /token " ,
741761 method: " POST " ,
742762 query: [ URLQueryItem ( name: " grant_type " , value: " refresh_token " ) ] ,
743- body: configuration. encoder. encode ( UserCredentials ( refreshToken : refreshToken ) )
763+ body: configuration. encoder. encode ( credentials )
744764 )
745765 ) . decoded ( as: Session . self, decoder: configuration. decoder)
746766
@@ -749,33 +769,41 @@ public actor GoTrueClient {
749769 . user. confirmedAt != nil
750770 {
751771 try await sessionManager. update ( session)
752- await eventEmitter. emit ( . tokenRefreshed)
772+ await eventEmitter. emit ( . tokenRefreshed, session : session )
753773 }
754774
755775 return session
756776 }
757777
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-
765778 private func emitInitialSession( forStreamWithID id: UUID ) async {
766- _debug ( " start " )
767- defer { _debug ( " end " ) }
768-
769779 let session = try ? await session
770- await eventEmitter. emit ( session != nil ? . signedIn : . signedOut , id)
780+ await eventEmitter. emit ( . initialSession , session , id)
771781 }
772782
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 )
779807 }
780808
781809 private func isImplicitGrantFlow( url: URL ) -> Bool {
@@ -793,6 +821,7 @@ public actor GoTrueClient {
793821}
794822
795823extension GoTrueClient {
824+ /// Notification posted when an auth state event is triggered.
796825 public static let didChangeAuthStateNotification = Notification . Name (
797826 " DID_CHANGE_AUTH_STATE_NOTIFICATION "
798827 )
0 commit comments