diff --git a/packages/gotrue/lib/src/gotrue_admin_api.dart b/packages/gotrue/lib/src/gotrue_admin_api.dart index 2f9a18272..b3cc71879 100644 --- a/packages/gotrue/lib/src/gotrue_admin_api.dart +++ b/packages/gotrue/lib/src/gotrue_admin_api.dart @@ -1,5 +1,6 @@ import 'package:gotrue/gotrue.dart'; import 'package:gotrue/src/fetch.dart'; +import 'package:gotrue/src/helper.dart'; import 'package:gotrue/src/types/auth_response.dart'; import 'package:gotrue/src/types/fetch_options.dart'; import 'package:http/http.dart'; @@ -70,6 +71,7 @@ class GoTrueAdminApi { /// /// This function should only be called on a server. Never expose your `service_role` key on the client. Future deleteUser(String id) async { + validateUuid(id); final options = GotrueRequestOptions(headers: _headers); await _fetch.request( '$_url/admin/users/$id', @@ -173,6 +175,7 @@ class GoTrueAdminApi { /// Gets the user by their id. Future getUserById(String uid) async { + validateUuid(uid); final options = GotrueRequestOptions(headers: _headers); final response = await _fetch.request( '$_url/admin/users/$uid', @@ -187,6 +190,7 @@ class GoTrueAdminApi { String uid, { required AdminUserAttributes attributes, }) async { + validateUuid(uid); final body = attributes.toJson(); final options = GotrueRequestOptions(headers: _headers, body: body); final response = await _fetch.request( diff --git a/packages/gotrue/lib/src/gotrue_admin_mfa_api.dart b/packages/gotrue/lib/src/gotrue_admin_mfa_api.dart index 5a441984b..81a2921a2 100644 --- a/packages/gotrue/lib/src/gotrue_admin_mfa_api.dart +++ b/packages/gotrue/lib/src/gotrue_admin_mfa_api.dart @@ -1,4 +1,5 @@ import 'fetch.dart'; +import 'helper.dart'; import 'types/fetch_options.dart'; import 'types/mfa.dart'; @@ -17,6 +18,8 @@ class GoTrueAdminMFAApi { Future listFactors( {required String userId}) async { + validateUuid(userId); + final data = await _fetch.request( '$_url/admin/users/$userId/factors', RequestMethodType.get, @@ -33,6 +36,9 @@ class GoTrueAdminMFAApi { required String userId, required String factorId, }) async { + validateUuid(userId); + validateUuid(factorId); + final data = await _fetch.request( '$_url/admin/users/$userId/factors/$factorId', RequestMethodType.delete, diff --git a/packages/gotrue/lib/src/helper.dart b/packages/gotrue/lib/src/helper.dart index c4d850d31..920a2b633 100644 --- a/packages/gotrue/lib/src/helper.dart +++ b/packages/gotrue/lib/src/helper.dart @@ -21,3 +21,12 @@ String generatePKCEChallenge(String verifier) { return base64UrlEncode(sha256.convert(ascii.encode(verifier)).bytes) .split('=')[0]; } + +final uuidRegex = + RegExp(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'); + +void validateUuid(String id) { + if (!uuidRegex.hasMatch(id)) { + throw ArgumentError('Invalid id: $id, must be a valid UUID'); + } +} diff --git a/packages/gotrue/test/admin_test.dart b/packages/gotrue/test/admin_test.dart index 76954d42a..488cabbc5 100644 --- a/packages/gotrue/test/admin_test.dart +++ b/packages/gotrue/test/admin_test.dart @@ -111,4 +111,40 @@ void main() { expect(userLengthBefore - 1, userLengthAfter); }); }); + + group('validates ids', () { + test('deleteUser() validates ids', () { + expect(() => client.admin.deleteUser('invalid-id'), + throwsA(isA())); + }); + + test('getUserById() validates ids', () { + expect(() => client.admin.getUserById('invalid-id'), + throwsA(isA())); + }); + + test('updateUserById() validates ids', () { + expect( + () => client.admin.updateUserById('invalid-id', + attributes: AdminUserAttributes(email: 'test@test.com')), + throwsA(isA())); + }); + + test('listFactors() validates ids', () { + expect(() => client.admin.mfa.listFactors(userId: 'invalid-id'), + throwsA(isA())); + }); + + test('deleteFactor() validates ids', () { + expect( + () => client.admin.mfa.deleteFactor( + userId: 'invalid-id', factorId: 'invalid-factor-id'), + throwsA(isA())); + + expect( + () => client.admin.mfa + .deleteFactor(userId: userId1, factorId: 'invalid-factor-id'), + throwsA(isA())); + }); + }); }