diff --git a/packages/core/auth-js/src/GoTrueAdminApi.ts b/packages/core/auth-js/src/GoTrueAdminApi.ts index 5ccbe074e..9c97f600b 100644 --- a/packages/core/auth-js/src/GoTrueAdminApi.ts +++ b/packages/core/auth-js/src/GoTrueAdminApi.ts @@ -21,6 +21,10 @@ import { PageParams, SIGN_OUT_SCOPES, SignOutScope, + GoTrueAdminOAuthApi, + CreateOAuthClientParams, + OAuthClientResponse, + OAuthClientListResponse, } from './lib/types' import { AuthError, isAuthError } from './lib/errors' @@ -28,6 +32,12 @@ export default class GoTrueAdminApi { /** Contains all MFA administration methods. */ mfa: GoTrueAdminMFAApi + /** + * Contains all OAuth client administration methods. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ + oauth: GoTrueAdminOAuthApi + protected url: string protected headers: { [key: string]: string @@ -52,6 +62,13 @@ export default class GoTrueAdminApi { listFactors: this._listFactors.bind(this), deleteFactor: this._deleteFactor.bind(this), } + this.oauth = { + listClients: this._listOAuthClients.bind(this), + createClient: this._createOAuthClient.bind(this), + getClient: this._getOAuthClient.bind(this), + deleteClient: this._deleteOAuthClient.bind(this), + regenerateClientSecret: this._regenerateOAuthClientSecret.bind(this), + } } /** @@ -349,4 +366,150 @@ export default class GoTrueAdminApi { throw error } } + + /** + * 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. + */ + private async _listOAuthClients(params?: PageParams): Promise { + try { + const pagination: Pagination = { nextPage: null, lastPage: 0, total: 0 } + const response = await _request(this.fetch, 'GET', `${this.url}/admin/oauth/clients`, { + headers: this.headers, + noResolveJson: true, + query: { + page: params?.page?.toString() ?? '', + per_page: params?.perPage?.toString() ?? '', + }, + xform: _noResolveJsonResponse, + }) + if (response.error) throw response.error + + const clients = await response.json() + const total = response.headers.get('x-total-count') ?? 0 + const links = response.headers.get('link')?.split(',') ?? [] + if (links.length > 0) { + links.forEach((link: string) => { + const page = parseInt(link.split(';')[0].split('=')[1].substring(0, 1)) + const rel = JSON.parse(link.split(';')[1].split('=')[1]) + pagination[`${rel}Page`] = page + }) + + pagination.total = parseInt(total) + } + return { data: { ...clients, ...pagination }, error: null } + } catch (error) { + if (isAuthError(error)) { + return { data: { clients: [] }, error } + } + throw error + } + } + + /** + * 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. + */ + private async _createOAuthClient( + params: CreateOAuthClientParams + ): Promise { + try { + return await _request(this.fetch, 'POST', `${this.url}/admin/oauth/clients`, { + body: params, + headers: this.headers, + xform: (client: any) => { + return { data: client, error: null } + }, + }) + } catch (error) { + if (isAuthError(error)) { + return { data: null, error } + } + + throw error + } + } + + /** + * 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. + */ + private async _getOAuthClient(clientId: string): Promise { + try { + return await _request(this.fetch, 'GET', `${this.url}/admin/oauth/clients/${clientId}`, { + headers: this.headers, + xform: (client: any) => { + return { data: client, error: null } + }, + }) + } catch (error) { + if (isAuthError(error)) { + return { data: null, error } + } + + throw error + } + } + + /** + * 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. + */ + private async _deleteOAuthClient(clientId: string): Promise { + try { + return await _request( + this.fetch, + 'DELETE', + `${this.url}/admin/oauth/clients/${clientId}`, + { + headers: this.headers, + xform: (client: any) => { + return { data: client, error: null } + }, + } + ) + } catch (error) { + if (isAuthError(error)) { + return { data: null, error } + } + + throw error + } + } + + /** + * 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. + */ + private async _regenerateOAuthClientSecret(clientId: string): Promise { + try { + return await _request( + this.fetch, + 'POST', + `${this.url}/admin/oauth/clients/${clientId}/regenerate_secret`, + { + headers: this.headers, + xform: (client: any) => { + return { data: client, error: null } + }, + } + ) + } catch (error) { + if (isAuthError(error)) { + return { data: null, error } + } + + throw error + } + } } diff --git a/packages/core/auth-js/src/lib/types.ts b/packages/core/auth-js/src/lib/types.ts index 46ef00b80..734d8ef0a 100644 --- a/packages/core/auth-js/src/lib/types.ts +++ b/packages/core/auth-js/src/lib/types.ts @@ -1448,3 +1448,145 @@ export interface JWK { export const SIGN_OUT_SCOPES = ['global', 'local', 'others'] as const export type SignOutScope = (typeof SIGN_OUT_SCOPES)[number] + +/** + * OAuth client grant types supported by the OAuth 2.1 server. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientGrantType = 'authorization_code' | 'refresh_token' + +/** + * OAuth client response types supported by the OAuth 2.1 server. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientResponseType = 'code' + +/** + * OAuth client type indicating whether the client can keep credentials confidential. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientType = 'public' | 'confidential' + +/** + * OAuth client registration type. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientRegistrationType = 'dynamic' | 'manual' + +/** + * OAuth client object returned from the OAuth 2.1 server. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClient = { + /** Unique identifier for the OAuth client */ + client_id: string + /** Human-readable name of the OAuth client */ + client_name: string + /** Client secret (only returned on registration and regeneration) */ + client_secret?: string + /** Type of OAuth client */ + client_type: OAuthClientType + /** Token endpoint authentication method */ + token_endpoint_auth_method: string + /** Registration type of the client */ + registration_type: OAuthClientRegistrationType + /** URI of the OAuth client */ + client_uri?: string + /** Array of allowed redirect URIs */ + redirect_uris: string[] + /** Array of allowed grant types */ + grant_types: OAuthClientGrantType[] + /** Array of allowed response types */ + response_types: OAuthClientResponseType[] + /** Scope of the OAuth client */ + scope?: string + /** Timestamp when the client was created */ + created_at: string + /** Timestamp when the client was last updated */ + updated_at: string +} + +/** + * Parameters for creating a new OAuth client. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type CreateOAuthClientParams = { + /** Human-readable name of the OAuth client */ + client_name: string + /** URI of the OAuth client */ + client_uri?: string + /** Array of allowed redirect URIs */ + redirect_uris: string[] + /** Array of allowed grant types (optional, defaults to authorization_code and refresh_token) */ + grant_types?: OAuthClientGrantType[] + /** Array of allowed response types (optional, defaults to code) */ + response_types?: OAuthClientResponseType[] + /** Scope of the OAuth client */ + scope?: string +} + +/** + * Response type for OAuth client operations. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientResponse = RequestResult + +/** + * Response type for listing OAuth clients. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export type OAuthClientListResponse = + | { + data: { clients: OAuthClient[]; aud: string } & Pagination + error: null + } + | { + data: { clients: [] } + error: AuthError + } + +/** + * Contains all OAuth client administration methods. + * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + */ +export interface GoTrueAdminOAuthApi { + /** + * 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. + */ + listClients(params?: PageParams): Promise + + /** + * 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. + */ + createClient(params: CreateOAuthClientParams): Promise + + /** + * 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. + */ + getClient(clientId: string): Promise + + /** + * 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. + */ + deleteClient(clientId: string): Promise + + /** + * 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. + */ + regenerateClientSecret(clientId: string): Promise +}