@@ -24,6 +24,13 @@ import 'counter.dart';
24
24
///
25
25
/// [realtimeClientOptions] specifies different options you can pass to `RealtimeClient` .
26
26
///
27
+ /// [accessToken] Optional function for using a third-party authentication system with Supabase.
28
+ /// The function should return an access token or ID token (JWT) by obtaining
29
+ /// it from the third-party auth client library. Note that this function may be
30
+ /// called concurrently and many times. Use memoization and locking techniques
31
+ /// if this is not supported by the client libraries. When set, the `auth`
32
+ /// namespace of the Supabase client cannot be used.
33
+ ///
27
34
/// Pass an instance of `YAJsonIsolate` to [isolate] to use your own persisted
28
35
/// isolate instance. A new instance will be created if [isolate] is omitted.
29
36
///
@@ -43,7 +50,7 @@ class SupabaseClient {
43
50
final Client ? _httpClient;
44
51
late final Client _authHttpClient;
45
52
46
- late final GoTrueClient auth ;
53
+ late final GoTrueClient _authInstance ;
47
54
48
55
/// Supabase Functions allows you to deploy and invoke edge functions.
49
56
late final FunctionsClient functions;
@@ -54,6 +61,7 @@ class SupabaseClient {
54
61
late final PostgrestClient rest;
55
62
late StreamSubscription <AuthState > _authStateSubscription;
56
63
late final YAJsonIsolate _isolate;
64
+ final Future <String > Function ()? accessToken;
57
65
58
66
/// Increment ID of the stream to create different realtime topic for each stream
59
67
final _incrementId = Counter ();
@@ -106,6 +114,7 @@ class SupabaseClient {
106
114
AuthClientOptions authOptions = const AuthClientOptions (),
107
115
StorageClientOptions storageOptions = const StorageClientOptions (),
108
116
RealtimeClientOptions realtimeClientOptions = const RealtimeClientOptions (),
117
+ this .accessToken,
109
118
Map <String , String >? headers,
110
119
Client ? httpClient,
111
120
YAJsonIsolate ? isolate,
@@ -122,18 +131,30 @@ class SupabaseClient {
122
131
},
123
132
_httpClient = httpClient,
124
133
_isolate = isolate ?? (YAJsonIsolate ()..initialize ()) {
125
- auth = _initSupabaseAuthClient (
134
+ _authInstance = _initSupabaseAuthClient (
126
135
autoRefreshToken: authOptions.autoRefreshToken,
127
136
gotrueAsyncStorage: authOptions.pkceAsyncStorage,
128
137
authFlowType: authOptions.authFlowType,
129
138
);
130
139
_authHttpClient =
131
- AuthHttpClient (_supabaseKey, httpClient ?? Client (), auth );
140
+ AuthHttpClient (_supabaseKey, httpClient ?? Client (), _getAccessToken );
132
141
rest = _initRestClient ();
133
142
functions = _initFunctionsClient ();
134
143
storage = _initStorageClient (storageOptions.retryAttempts);
135
144
realtime = _initRealtimeClient (options: realtimeClientOptions);
136
- _listenForAuthEvents ();
145
+ if (accessToken != null ) {
146
+ _listenForAuthEvents ();
147
+ }
148
+ }
149
+
150
+ GoTrueClient get auth {
151
+ if (accessToken == null ) {
152
+ return _authInstance;
153
+ } else {
154
+ throw AuthException (
155
+ 'Supabase Client is configured with the accessToken option, accessing supabase.auth is not possible.' ,
156
+ );
157
+ }
137
158
}
138
159
139
160
/// Perform a table operation.
@@ -200,6 +221,31 @@ class SupabaseClient {
200
221
return realtime.removeAllChannels ();
201
222
}
202
223
224
+ Future <String ?> _getAccessToken () async {
225
+ if (accessToken != null ) {
226
+ return await accessToken !();
227
+ }
228
+
229
+ if (_authInstance.currentSession? .isExpired ?? false ) {
230
+ try {
231
+ await _authInstance.refreshSession ();
232
+ return _authInstance.currentSession? .accessToken;
233
+ } catch (error) {
234
+ final expiresAt = _authInstance.currentSession? .expiresAt;
235
+ if (expiresAt != null ) {
236
+ // Failed to refresh the token.
237
+ final isExpiredWithoutMargin = DateTime .now ()
238
+ .isAfter (DateTime .fromMillisecondsSinceEpoch (expiresAt * 1000 ));
239
+ if (isExpiredWithoutMargin) {
240
+ // Throw the error instead of making an API request with an expired token.
241
+ rethrow ;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ return null ;
247
+ }
248
+
203
249
Future <void > dispose () async {
204
250
await _authStateSubscription.cancel ();
205
251
await _isolate.dispose ();
0 commit comments