Skip to content
Merged
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
2 changes: 1 addition & 1 deletion infra/gotrue/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
31 changes: 31 additions & 0 deletions packages/gotrue/lib/src/gotrue_admin_oauth_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@ class OAuthClientResponse {
class OAuthClientListResponse {
final List<OAuthClient> 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<String, dynamic> json) {
Expand All @@ -34,6 +40,9 @@ class OAuthClientListResponse {
.map((e) => OAuthClient.fromJson(e as Map<String, dynamic>))
.toList(),
aud: json['aud'] as String?,
nextPage: json['nextPage'] as int?,
lastPage: json['lastPage'] as int?,
total: json['total'] as int? ?? 0,
);
}
}
Expand Down Expand Up @@ -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<OAuthClientResponse> 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.
///
Expand Down
44 changes: 44 additions & 0 deletions packages/gotrue/lib/src/types/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>? redirectUris;

/// Array of allowed grant types
final List<OAuthClientGrantType>? grantTypes;

/// Array of allowed response types
final List<OAuthClientResponseType>? responseTypes;

/// Scope of the OAuth client
final String? scope;

UpdateOAuthClientParams({
this.clientName,
this.clientUri,
this.redirectUris,
this.grantTypes,
this.responseTypes,
this.scope,
});

Map<String, dynamic> 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,
};
}
}
30 changes: 30 additions & 0 deletions packages/gotrue/test/src/gotrue_admin_oauth_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -127,5 +151,11 @@ void main() {
expect(() => client.admin.oauth.regenerateClientSecret('invalid-id'),
throwsA(isA<ArgumentError>()));
});

test('updateClient() validates ids', () {
final params = UpdateOAuthClientParams(clientName: 'Updated Name');
expect(() => client.admin.oauth.updateClient('invalid-id', params),
throwsA(isA<ArgumentError>()));
});
});
}