diff --git a/infra/gotrue/docker-compose.yml b/infra/gotrue/docker-compose.yml index e4fa26bd..308094d3 100644 --- a/infra/gotrue/docker-compose.yml +++ b/infra/gotrue/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: gotrue: # Signup enabled, autoconfirm on - image: supabase/auth:v2.180.0 + image: supabase/auth:v2.182.1 ports: - '9998:9998' environment: diff --git a/packages/gotrue/lib/src/gotrue_admin_oauth_api.dart b/packages/gotrue/lib/src/gotrue_admin_oauth_api.dart index 76d8cd70..afb07891 100644 --- a/packages/gotrue/lib/src/gotrue_admin_oauth_api.dart +++ b/packages/gotrue/lib/src/gotrue_admin_oauth_api.dart @@ -22,10 +22,16 @@ class OAuthClientResponse { class OAuthClientListResponse { final List clients; final String? aud; + final int? nextPage; + final int? lastPage; + final int total; OAuthClientListResponse({ required this.clients, this.aud, + this.nextPage, + this.lastPage, + this.total = 0, }); factory OAuthClientListResponse.fromJson(Map json) { @@ -34,6 +40,9 @@ class OAuthClientListResponse { .map((e) => OAuthClient.fromJson(e as Map)) .toList(), aud: json['aud'] as String?, + nextPage: json['nextPage'] as int?, + lastPage: json['lastPage'] as int?, + total: json['total'] as int? ?? 0, ); } } @@ -113,6 +122,28 @@ class GoTrueAdminOAuthApi { return OAuthClientResponse.fromJson(data); } + /// Updates an existing OAuth client. + /// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + /// + /// This function should only be called on a server. Never expose your `service_role` key in the browser. + Future updateClient( + String clientId, + UpdateOAuthClientParams params, + ) async { + validateUuid(clientId); + + final data = await _fetch.request( + '$_url/admin/oauth/clients/$clientId', + RequestMethodType.put, + options: GotrueRequestOptions( + headers: _headers, + body: params.toJson(), + ), + ); + + return OAuthClientResponse.fromJson(data); + } + /// Deletes an OAuth client. /// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. /// diff --git a/packages/gotrue/lib/src/types/types.dart b/packages/gotrue/lib/src/types/types.dart index ea33b680..82a65344 100644 --- a/packages/gotrue/lib/src/types/types.dart +++ b/packages/gotrue/lib/src/types/types.dart @@ -208,3 +208,47 @@ class CreateOAuthClientParams { }; } } + +/// Parameters for updating an existing OAuth client. +/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. +class UpdateOAuthClientParams { + /// Human-readable name of the OAuth client + final String? clientName; + + /// URI of the OAuth client + final String? clientUri; + + /// Array of allowed redirect URIs + final List? redirectUris; + + /// Array of allowed grant types + final List? grantTypes; + + /// Array of allowed response types + final List? responseTypes; + + /// Scope of the OAuth client + final String? scope; + + UpdateOAuthClientParams({ + this.clientName, + this.clientUri, + this.redirectUris, + this.grantTypes, + this.responseTypes, + this.scope, + }); + + Map toJson() { + return { + if (clientName != null) 'client_name': clientName, + if (clientUri != null) 'client_uri': clientUri, + if (redirectUris != null) 'redirect_uris': redirectUris, + if (grantTypes != null) + 'grant_types': grantTypes!.map((e) => e.value).toList(), + if (responseTypes != null) + 'response_types': responseTypes!.map((e) => e.value).toList(), + if (scope != null) 'scope': scope, + }; + } +} diff --git a/packages/gotrue/test/src/gotrue_admin_oauth_api_test.dart b/packages/gotrue/test/src/gotrue_admin_oauth_api_test.dart index cfa295c6..dfb16d82 100644 --- a/packages/gotrue/test/src/gotrue_admin_oauth_api_test.dart +++ b/packages/gotrue/test/src/gotrue_admin_oauth_api_test.dart @@ -80,6 +80,30 @@ void main() { expect(res.client?.clientName, 'Test OAuth Client for Get'); }); + test('update OAuth client', () async { + // First create a client + final createParams = CreateOAuthClientParams( + clientName: 'Test OAuth Client for Update', + redirectUris: ['https://example.com/callback'], + ); + final createRes = await client.admin.oauth.createClient(createParams); + final clientId = createRes.client!.clientId; + + // Update the client + final updateParams = UpdateOAuthClientParams( + clientName: 'Updated OAuth Client Name', + ); + final updateRes = + await client.admin.oauth.updateClient(clientId, updateParams); + expect(updateRes.client, isNotNull); + expect(updateRes.client?.clientId, clientId); + expect(updateRes.client?.clientName, 'Updated OAuth Client Name'); + + // Verify the update by getting the client again + final getRes = await client.admin.oauth.getClient(clientId); + expect(getRes.client?.clientName, 'Updated OAuth Client Name'); + }); + test('regenerate OAuth client secret', () async { // First create a client final params = CreateOAuthClientParams( @@ -127,5 +151,11 @@ void main() { expect(() => client.admin.oauth.regenerateClientSecret('invalid-id'), throwsA(isA())); }); + + test('updateClient() validates ids', () { + final params = UpdateOAuthClientParams(clientName: 'Updated Name'); + expect(() => client.admin.oauth.updateClient('invalid-id', params), + throwsA(isA())); + }); }); }