Skip to content

Commit

Permalink
fix(supabase)!: make Supabase credentials private in SupabaseClient (
Browse files Browse the repository at this point in the history
…#649)

* make supabase key and supabase URL private

* make rest and realtime private

* minor dx fix with how supabase key is passes

* fix:revert realtime fix

* reverse order of isClosed and isErrored

* remove extra space

* make realtime and rest public
  • Loading branch information
dshukertjr committed Oct 4, 2023
1 parent 2f9fe41 commit fa341bf
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 43 deletions.
42 changes: 13 additions & 29 deletions packages/supabase/lib/src/supabase_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ import 'auth_http_client.dart';
/// `AuthFlowType.pkce`in order to perform auth actions with pkce flow.
/// {@endtemplate}
class SupabaseClient {
final String supabaseUrl;
final String supabaseKey;
final String _supabaseKey;
final PostgrestClientOptions _postgrestOptions;

final String _restUrl;
Expand Down Expand Up @@ -99,35 +98,19 @@ class SupabaseClient {
..addAll(_headers);
}

/// Creates a Supabase client to interact with your Supabase instance.
///
/// [supabaseUrl] and [supabaseKey] can be found on your Supabase dashboard.
///
/// You can access none public schema by passing different [schema].
///
/// Default headers can be overridden by specifying [headers].
///
/// Custom http client can be used by passing [httpClient] parameter.
///
/// [storageRetryAttempts] specifies how many retry attempts there should be to
/// upload a file to Supabase storage when failed due to network interruption.
///
/// [realtimeClientOptions] specifies different options you can pass to `RealtimeClient`.
///
/// Pass an instance of `YAJsonIsolate` to [isolate] to use your own persisted
/// isolate instance. A new instance will be created if [isolate] is omitted.
/// {@macro supabase_client}
SupabaseClient(
this.supabaseUrl,
this.supabaseKey, {
String supabaseUrl,
String supabaseKey, {
PostgrestClientOptions postgrestOptions = const PostgrestClientOptions(),
AuthClientOptions authOptions = const AuthClientOptions(),
StorageClientOptions storageOptions = const StorageClientOptions(),
RealtimeClientOptions realtimeClientOptions = const RealtimeClientOptions(),
Map<String, String>? headers,
Client? httpClient,
YAJsonIsolate? isolate,
}) : _restUrl = '$supabaseUrl/rest/v1',
}) : _supabaseKey = supabaseKey,
_restUrl = '$supabaseUrl/rest/v1',
_realtimeUrl = '$supabaseUrl/realtime/v1'.replaceAll('http', 'ws'),
_authUrl = '$supabaseUrl/auth/v1',
_storageUrl = '$supabaseUrl/storage/v1',
Expand All @@ -144,7 +127,8 @@ class SupabaseClient {
gotrueAsyncStorage: authOptions.pkceAsyncStorage,
authFlowType: authOptions.authFlowType,
);
_authHttpClient = AuthHttpClient(supabaseKey, httpClient ?? Client(), auth);
_authHttpClient =
AuthHttpClient(_supabaseKey, httpClient ?? Client(), auth);
rest = _initRestClient();
functions = _initFunctionsClient();
storage = _initStorageClient(storageOptions.retryAttempts);
Expand Down Expand Up @@ -218,8 +202,8 @@ class SupabaseClient {
required AuthFlowType authFlowType,
}) {
final authHeaders = {...headers};
authHeaders['apikey'] = supabaseKey;
authHeaders['Authorization'] = 'Bearer $supabaseKey';
authHeaders['apikey'] = _supabaseKey;
authHeaders['Authorization'] = 'Bearer $_supabaseKey';

return GoTrueClient(
url: _authUrl,
Expand Down Expand Up @@ -266,7 +250,7 @@ class SupabaseClient {
return RealtimeClient(
_realtimeUrl,
params: {
'apikey': supabaseKey,
'apikey': _supabaseKey,
if (eventsPerSecond != null) 'eventsPerSecond': '$eventsPerSecond'
},
headers: headers,
Expand All @@ -275,9 +259,9 @@ class SupabaseClient {
}

Map<String, String> _getAuthHeaders() {
final authBearer = auth.currentSession?.accessToken ?? supabaseKey;
final authBearer = auth.currentSession?.accessToken ?? _supabaseKey;
final defaultHeaders = {
'apikey': supabaseKey,
'apikey': _supabaseKey,
'Authorization': 'Bearer $authBearer',
};
final headers = {...defaultHeaders, ..._headers};
Expand Down Expand Up @@ -305,7 +289,7 @@ class SupabaseClient {
event == AuthChangeEvent.userDeleted) {
// Token is removed

realtime.setAuth(supabaseKey);
realtime.setAuth(_supabaseKey);
}
}
}
78 changes: 66 additions & 12 deletions packages/supabase/test/client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,44 @@ import 'package:test/test.dart';
import 'utils.dart';

void main() {
/// Extracts a single request sent to the realtime server
Future<HttpRequest> getRealtimeRequest({
required HttpServer server,
required SupabaseClient supabaseClient,
}) async {
supabaseClient.channel('name').subscribe();

return server.first;
}

group('Standard Header', () {
const supabaseUrl = 'https://nlbsnpoablmsiwndbmer.supabase.co';
late String supabaseUrl;
const supabaseKey =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im53emxkenlsb2pyemdqemloZHJrIiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODQxMzI2ODAsImV4cCI6MTk5OTcwODY4MH0.MU-LVeAPic93VLcRsHktxzYtBKBUMWAQb8E-0AQETPs';
late SupabaseClient client;
late HttpServer mockServer;

setUp(() async {
mockServer = await HttpServer.bind('localhost', 0);
supabaseUrl = 'http://${mockServer.address.host}:${mockServer.port}';

setUp(() {
client = SupabaseClient(supabaseUrl, supabaseKey);
});

tearDown(() async {
await client.removeAllChannels();
await client.dispose();
});

test('X-Client-Info header is set properly on realtime', () {
test('X-Client-Info header is set properly on realtime', () async {
final request = await getRealtimeRequest(
server: mockServer,
supabaseClient: client,
);

final xClientHeaderBeforeSlash =
client.realtime.headers['X-Client-Info']!.split('/').first;
request.headers['X-Client-Info']?.first.split('/').first;

expect(xClientHeaderBeforeSlash, 'supabase-dart');
});

Expand All @@ -32,19 +53,33 @@ void main() {
expect(xClientHeaderBeforeSlash, 'supabase-dart');
});

test('realtime URL is properly being set', () {
var realtimeWebsocketURL = Uri.parse(client.realtime.endPointURL);
test('realtime URL is properly being set', () async {
final request = await getRealtimeRequest(
server: mockServer,
supabaseClient: client,
);

var realtimeWebsocketURL = request.uri;

expect(
realtimeWebsocketURL.queryParameters,
containsPair('apikey', supabaseKey),
);
expect(realtimeWebsocketURL.queryParameters['log_level'], isNull);
});

test('log_level query parameter is properly set', () async {
client = SupabaseClient(supabaseUrl, supabaseKey,
realtimeClientOptions:
RealtimeClientOptions(logLevel: RealtimeLogLevel.info));

realtimeWebsocketURL = Uri.parse(client.realtime.endPointURL);
final request = await getRealtimeRequest(
server: mockServer,
supabaseClient: client,
);

final realtimeWebsocketURL = request.uri;

expect(
realtimeWebsocketURL.queryParameters,
containsPair('apikey', supabaseKey),
Expand All @@ -55,8 +90,13 @@ void main() {
);
});

test('realtime access token is set properly', () {
expect(client.realtime.accessToken, supabaseKey);
test('realtime access token is set properly', () async {
final request = await getRealtimeRequest(
server: mockServer,
supabaseClient: client,
);

expect(request.uri.queryParameters['apikey'], supabaseKey);
});
});

Expand Down Expand Up @@ -163,9 +203,23 @@ void main() {
);
});

test('X-Client-Info header is set properly on realtime', () {
final xClientInfoHeader = client.realtime.headers['X-Client-Info'];
expect(xClientInfoHeader, 'supabase-flutter/0.0.0');
test('X-Client-Info header is set properly on realtime', () async {
final mockServer = await HttpServer.bind('localhost', 0);

final client = SupabaseClient(
'http://${mockServer.address.host}:${mockServer.port}',
supabaseKey,
headers: {
'X-Client-Info': 'supabase-flutter/0.0.0',
},
);

final request = await getRealtimeRequest(
server: mockServer,
supabaseClient: client,
);

expect(request.headers['X-Client-Info']?.first, 'supabase-flutter/0.0.0');
});

test('X-Client-Info header is set properly on storage', () {
Expand Down
4 changes: 2 additions & 2 deletions packages/supabase/test/mock_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,8 @@ void main() {
await customHeadersClient.dispose();

//Manually disconnect the socket channel to avoid automatic retrying to reconnect. This caused failing in later executed tests.
client.realtime.disconnect();
customHeadersClient.realtime.disconnect();
await client.removeAllChannels();
await customHeadersClient.removeAllChannels();

// Wait for the realtime updates to come through
await Future.delayed(Duration(milliseconds: 100));
Expand Down

0 comments on commit fa341bf

Please sign in to comment.