Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions packages/gotrue/lib/src/gotrue_admin_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:gotrue/src/types/fetch_options.dart';
import 'package:http/http.dart';

import 'gotrue_admin_mfa_api.dart';
import 'gotrue_admin_oauth_api.dart';

class GoTrueAdminApi {
final String _url;
Expand All @@ -15,6 +16,10 @@ class GoTrueAdminApi {
late final GotrueFetch _fetch = GotrueFetch(_httpClient);
late final GoTrueAdminMFAApi mfa;

/// Contains all OAuth client administration methods.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
late final GoTrueAdminOAuthApi oauth;

GoTrueAdminApi(
this._url, {
Map<String, String>? headers,
Expand All @@ -26,6 +31,11 @@ class GoTrueAdminApi {
headers: _headers,
fetch: _fetch,
);
oauth = GoTrueAdminOAuthApi(
url: _url,
headers: _headers,
fetch: _fetch,
);
}

/// Removes a logged-in session.
Expand Down
151 changes: 151 additions & 0 deletions packages/gotrue/lib/src/gotrue_admin_oauth_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import 'fetch.dart';
import 'helper.dart';
import 'types/fetch_options.dart';
import 'types/types.dart';

/// Response type for OAuth client operations.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
class OAuthClientResponse {
final OAuthClient? client;

OAuthClientResponse({this.client});

factory OAuthClientResponse.fromJson(Map<String, dynamic> json) {
return OAuthClientResponse(
client: OAuthClient.fromJson(json),
);
}
}

/// Response type for listing OAuth clients.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
class OAuthClientListResponse {
final List<OAuthClient> clients;
final String aud;

OAuthClientListResponse({
required this.clients,
required this.aud,
});

factory OAuthClientListResponse.fromJson(Map<String, dynamic> json) {
return OAuthClientListResponse(
clients: (json['clients'] as List)
.map((e) => OAuthClient.fromJson(e as Map<String, dynamic>))
.toList(),
aud: json['aud'] as String,
);
}
}

/// Contains all OAuth client administration methods.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
class GoTrueAdminOAuthApi {
final String _url;
final Map<String, String> _headers;
final GotrueFetch _fetch;

GoTrueAdminOAuthApi({
required String url,
required Map<String, String> headers,
required GotrueFetch fetch,
}) : _url = url,
_headers = headers,
_fetch = fetch;

/// Lists all OAuth clients with optional pagination.
/// 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<OAuthClientListResponse> listClients({
int? page,
int? perPage,
}) async {
final data = await _fetch.request(
'$_url/admin/oauth/clients',
RequestMethodType.get,
options: GotrueRequestOptions(
headers: _headers,
query: {
if (page != null) 'page': page.toString(),
if (perPage != null) 'per_page': perPage.toString(),
},
),
);

return OAuthClientListResponse.fromJson(data);
}

/// Creates a new 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> createClient(
CreateOAuthClientParams params,
) async {
final data = await _fetch.request(
'$_url/admin/oauth/clients',
RequestMethodType.post,
options: GotrueRequestOptions(
headers: _headers,
body: params.toJson(),
),
);

return OAuthClientResponse.fromJson(data);
}

/// Gets details of a specific 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> getClient(String clientId) async {
validateUuid(clientId);

final data = await _fetch.request(
'$_url/admin/oauth/clients/$clientId',
RequestMethodType.get,
options: GotrueRequestOptions(
headers: _headers,
),
);

return OAuthClientResponse.fromJson(data);
}

/// Deletes an 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> deleteClient(String clientId) async {
validateUuid(clientId);

final data = await _fetch.request(
'$_url/admin/oauth/clients/$clientId',
RequestMethodType.delete,
options: GotrueRequestOptions(
headers: _headers,
),
);

return OAuthClientResponse.fromJson(data);
}

/// Regenerates the secret for an 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> regenerateClientSecret(String clientId) async {
validateUuid(clientId);

final data = await _fetch.request(
'$_url/admin/oauth/clients/$clientId/regenerate_secret',
RequestMethodType.post,
options: GotrueRequestOptions(
headers: _headers,
),
);

return OAuthClientResponse.fromJson(data);
}
}
176 changes: 176 additions & 0 deletions packages/gotrue/lib/src/types/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,179 @@ enum OAuthProvider {
workos,
zoom,
}

/// OAuth client grant types supported by the OAuth 2.1 server.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
enum OAuthClientGrantType {
authorizationCode('authorization_code'),
refreshToken('refresh_token');

final String value;
const OAuthClientGrantType(this.value);
}

/// OAuth client response types supported by the OAuth 2.1 server.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
enum OAuthClientResponseType {
code('code');

final String value;
const OAuthClientResponseType(this.value);
}

/// OAuth client type indicating whether the client can keep credentials confidential.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
enum OAuthClientType {
public('public'),
confidential('confidential');

final String value;
const OAuthClientType(this.value);

static OAuthClientType fromString(String value) {
return OAuthClientType.values.firstWhere((e) => e.value == value);
}
}

/// OAuth client registration type.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
enum OAuthClientRegistrationType {
dynamic('dynamic'),
manual('manual');

final String value;
const OAuthClientRegistrationType(this.value);

static OAuthClientRegistrationType fromString(String value) {
return OAuthClientRegistrationType.values
.firstWhere((e) => e.value == value);
}
}

/// OAuth client object returned from the OAuth 2.1 server.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
class OAuthClient {
/// Unique identifier for the OAuth client
final String clientId;

/// Human-readable name of the OAuth client
final String clientName;

/// Client secret (only returned on registration and regeneration)
final String? clientSecret;

/// Type of OAuth client
final OAuthClientType clientType;

/// Token endpoint authentication method
final String tokenEndpointAuthMethod;

/// Registration type of the client
final OAuthClientRegistrationType registrationType;

/// 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;

/// Timestamp when the client was created
final String createdAt;

/// Timestamp when the client was last updated
final String updatedAt;

OAuthClient({
required this.clientId,
required this.clientName,
this.clientSecret,
required this.clientType,
required this.tokenEndpointAuthMethod,
required this.registrationType,
this.clientUri,
required this.redirectUris,
required this.grantTypes,
required this.responseTypes,
this.scope,
required this.createdAt,
required this.updatedAt,
});

factory OAuthClient.fromJson(Map<String, dynamic> json) {
return OAuthClient(
clientId: json['client_id'] as String,
clientName: json['client_name'] as String,
clientSecret: json['client_secret'] as String?,
clientType: OAuthClientType.fromString(json['client_type'] as String),
tokenEndpointAuthMethod: json['token_endpoint_auth_method'] as String,
registrationType: OAuthClientRegistrationType.fromString(
json['registration_type'] as String),
clientUri: json['client_uri'] as String?,
redirectUris: (json['redirect_uris'] as List).cast<String>(),
grantTypes: (json['grant_types'] as List)
.map((e) => OAuthClientGrantType.values
.firstWhere((gt) => gt.value == e as String))
.toList(),
responseTypes: (json['response_types'] as List)
.map((e) => OAuthClientResponseType.values
.firstWhere((rt) => rt.value == e as String))
.toList(),
scope: json['scope'] as String?,
createdAt: json['created_at'] as String,
updatedAt: json['updated_at'] as String,
);
}
}

/// Parameters for creating a new OAuth client.
/// Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
class CreateOAuthClientParams {
/// 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 (optional, defaults to authorization_code and refresh_token)
final List<OAuthClientGrantType>? grantTypes;

/// Array of allowed response types (optional, defaults to code)
final List<OAuthClientResponseType>? responseTypes;

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

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

Map<String, dynamic> toJson() {
return {
'client_name': clientName,
if (clientUri != null) 'client_uri': clientUri,
'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,
};
}
}
Loading
Loading