Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/gotrue/lib/src/gotrue_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ class GoTrueClient {
Client? httpClient,
GotrueAsyncStorage? asyncStorage,
AuthFlowType flowType = AuthFlowType.pkce,
}) : _url = url ?? Constants.defaultGotrueUrl,
}) : assert(
flowType != AuthFlowType.pkce || asyncStorage != null,
'You need to provide asyncStorage to perform pkce flow.',
),
_url = url ?? Constants.defaultGotrueUrl,
_headers = {...Constants.defaultHeaders, ...?headers},
_httpClient = httpClient,
_asyncStorage = asyncStorage,
Expand Down
1 change: 1 addition & 0 deletions packages/gotrue/test/admin_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void main() {
'Authorization': 'Bearer ${getServiceRoleToken(env)}',
'apikey': getServiceRoleToken(env),
},
asyncStorage: TestAsyncStorage(),
);
});

Expand Down
35 changes: 33 additions & 2 deletions packages/gotrue/test/client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ void main() {
final newClient = GoTrueClient(
url: gotrueUrl,
headers: {'apikey': anonToken},
flowType: AuthFlowType.implicit,
);

expect(newClient.currentSession?.refreshToken ?? '', isEmpty);
Expand Down Expand Up @@ -301,6 +302,7 @@ void main() {
final newClient = GoTrueClient(
url: gotrueUrl,
headers: {'apikey': anonToken},
flowType: AuthFlowType.implicit,
);

expect(newClient.currentSession, isNull);
Expand Down Expand Up @@ -344,6 +346,7 @@ void main() {
final newClient = GoTrueClient(
url: gotrueUrl,
headers: {'apikey': anonToken},
flowType: AuthFlowType.implicit,
);

// Should fall back to _callRefreshToken and succeed.
Expand Down Expand Up @@ -395,6 +398,7 @@ void main() {
final newClient = GoTrueClient(
url: gotrueUrl,
headers: {'apikey': anonToken},
flowType: AuthFlowType.implicit,
);

expect(newClient.currentSession, isNull);
Expand Down Expand Up @@ -607,7 +611,11 @@ void main() {
late GoTrueClient client;

setUpAll(() {
client = GoTrueClient(url: gotrueUrl, httpClient: CustomHttpClient());
client = GoTrueClient(
url: gotrueUrl,
httpClient: CustomHttpClient(),
flowType: AuthFlowType.implicit,
);
});

test('signIn()', () async {
Expand All @@ -629,7 +637,11 @@ void main() {

setUpAll(() {
httpClient = RetryTestHttpClient();
client = GoTrueClient(url: gotrueUrl, httpClient: httpClient);
client = GoTrueClient(
url: gotrueUrl,
httpClient: httpClient,
flowType: AuthFlowType.implicit,
);
});

test('Session recovery succeeds after retries', () async {
Expand Down Expand Up @@ -793,4 +805,23 @@ void main() {
await sub.cancel();
});
});

group('PKCE constructor assertion', () {
test(
'throws AssertionError if asyncStorage is missing when using PKCE flow',
() {
expect(
() => GoTrueClient(
url: gotrueUrl,
headers: {'Authorization': 'Bearer $anonToken', 'apikey': anonToken},
// asyncStorage is missing/null, and flowType defaults to PKCE
),
throwsA(isA<AssertionError>().having(
(e) => e.message,
'message',
contains('You need to provide asyncStorage to perform pkce flow.'),
)),
);
});
});
}
3 changes: 3 additions & 0 deletions packages/gotrue/test/header_isolation_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:convert';

import 'utils.dart';

import 'package:gotrue/gotrue.dart';
import 'package:http/http.dart';
import 'package:test/test.dart';
Expand Down Expand Up @@ -42,6 +44,7 @@ void main() {
url: 'http://localhost',
headers: {'apikey': 'anon-key'},
httpClient: http,
asyncStorage: TestAsyncStorage(),
);
});

Expand Down
1 change: 1 addition & 0 deletions packages/gotrue/test/src/gotrue_admin_mfa_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void main() {
'apikey': serviceRoleToken,
'x-forwarded-for': '127.0.0.1'
},
asyncStorage: TestAsyncStorage(),
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void main() {
'apikey': serviceRoleToken,
'x-forwarded-for': '127.0.0.1'
},
asyncStorage: TestAsyncStorage(),
);
});

Expand Down
1 change: 1 addition & 0 deletions packages/gotrue/test/src/gotrue_mfa_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void main() {
'apikey': anonToken,
'x-forwarded-for': '127.0.0.1'
},
asyncStorage: TestAsyncStorage(),
);
});

Expand Down
28 changes: 27 additions & 1 deletion packages/supabase/test/client_test.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'dart:async';
import 'dart:io';

import 'package:supabase/supabase.dart';
import 'package:supabase/supabase.dart' hide SupabaseClient;
import 'package:supabase/src/supabase_client.dart' as real;
import 'package:test/test.dart';
import 'package:yet_another_json_isolate/yet_another_json_isolate.dart';

Expand Down Expand Up @@ -399,3 +400,28 @@ void main() {
});
});
}

class SupabaseClient extends real.SupabaseClient {
SupabaseClient(
super.supabaseUrl,
super.supabaseKey, {
super.postgrestOptions,
AuthClientOptions authOptions = const AuthClientOptions(),
super.storageOptions,
super.functionsOptions,
super.realtimeClientOptions,
super.accessToken,
super.headers,
super.httpClient,
super.isolate,
}) : super(
authOptions: AuthClientOptions(
autoRefreshToken: authOptions.autoRefreshToken,
pkceAsyncStorage: authOptions.pkceAsyncStorage,
authFlowType: authOptions.authFlowType == AuthFlowType.pkce &&
authOptions.pkceAsyncStorage == null
? AuthFlowType.implicit
: authOptions.authFlowType,
),
);
}
8 changes: 8 additions & 0 deletions packages/supabase/test/mock_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,13 @@ void main() {
headers: {
'X-Client-Info': 'supabase-flutter/0.0.0',
},
authOptions: const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);
customHeadersClient = SupabaseClient(
'http://${mockServer.address.host}:${mockServer.port}',
apiKey,
headers: {'X-Client-Info': 'supabase-flutter/0.0.0', ...customHeaders},
authOptions: const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);
hasListener = false;
});
Expand Down Expand Up @@ -786,6 +788,8 @@ void main() {
'http://${errorServer.address.host}:${errorServer.port}',
'test-key',
headers: {'X-Client-Info': 'supabase-flutter/0.0.0'},
authOptions:
const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);

final stream = errorClient.from('todos').stream(primaryKey: ['id']);
Expand Down Expand Up @@ -817,6 +821,8 @@ void main() {
throw Exception('Token retrieval failed');
},
headers: {'X-Client-Info': 'supabase-flutter/0.0.0'},
authOptions:
const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);

// Should handle token errors gracefully
Expand All @@ -835,6 +841,8 @@ void main() {
'http://${mockServer.address.host}:${mockServer.port}',
'test-key',
headers: {'X-Client-Info': 'supabase-flutter/0.0.0'},
authOptions:
const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);

// First dispose should succeed
Expand Down
2 changes: 2 additions & 0 deletions packages/supabase/test/realtime_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ void main() {
supabase = SupabaseClient(
'http://${mockServer.address.host}:${mockServer.port}',
'supabaseKey',
authOptions:
const AuthClientOptions(authFlowType: AuthFlowType.implicit),
);

channel = supabase.channel('realtime');
Expand Down
20 changes: 20 additions & 0 deletions packages/supabase/test/utils.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:convert';

import 'package:supabase/supabase.dart';

/// Construct session data for a given expiration date
({String accessToken, String sessionString}) getSessionData(DateTime dateTime) {
final expiresAt = dateTime.millisecondsSinceEpoch ~/ 1000;
Expand All @@ -10,3 +12,21 @@ import 'dart:convert';
'{"access_token":"$accessToken","expires_in":${dateTime.difference(DateTime.now()).inSeconds},"refresh_token":"-yeS4omysFs9tpUYBws9Rg","token_type":"bearer","provider_token":null,"provider_refresh_token":null,"user":{"id":"4d2583da-8de4-49d3-9cd1-37a9a74f55bd","app_metadata":{"provider":"email","providers":["email"]},"user_metadata":{"Hello":"World"},"aud":"","email":"fake1680338105@email.com","phone":"","created_at":"2023-04-01T08:35:05.208586Z","confirmed_at":null,"email_confirmed_at":"2023-04-01T08:35:05.220096086Z","phone_confirmed_at":null,"last_sign_in_at":"2023-04-01T08:35:05.222755878Z","role":"","updated_at":"2023-04-01T08:35:05.226938Z"}}';
return (accessToken: accessToken, sessionString: sessionString);
}

class TestAsyncStorage extends GotrueAsyncStorage {
final Map<String, String> _map = {};
@override
Future<String?> getItem({required String key}) async {
return _map[key];
}

@override
Future<void> removeItem({required String key}) async {
_map.remove(key);
}

@override
Future<void> setItem({required String key, required String value}) async {
_map[key] = value;
}
}
Loading