Skip to content
Open
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
77 changes: 77 additions & 0 deletions packages/core/auth-js/src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ import type {
AuthOAuthServerApi,
AuthOAuthAuthorizationDetailsResponse,
AuthOAuthConsentResponse,
AuthOAuthGrantsResponse,
AuthOAuthRevokeGrantResponse,
Prettify,
Provider,
ResendParams,
Expand Down Expand Up @@ -339,6 +341,8 @@ export default class GoTrueClient {
getAuthorizationDetails: this._getAuthorizationDetails.bind(this),
approveAuthorization: this._approveAuthorization.bind(this),
denyAuthorization: this._denyAuthorization.bind(this),
listGrants: this._listOAuthGrants.bind(this),
revokeGrant: this._revokeOAuthGrant.bind(this),
}

if (this.persistSession) {
Expand Down Expand Up @@ -3544,6 +3548,79 @@ export default class GoTrueClient {
}
}

/**
* Lists all OAuth grants that the authenticated user has authorized.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*/
private async _listOAuthGrants(): Promise<AuthOAuthGrantsResponse> {
try {
return await this._useSession(async (result) => {
const {
data: { session },
error: sessionError,
} = result

if (sessionError) {
return this._returnResult({ data: null, error: sessionError })
}

if (!session) {
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
}

return await _request(this.fetch, 'GET', `${this.url}/user/oauth/grants`, {
headers: this.headers,
jwt: session.access_token,
xform: (data: any) => ({ data, error: null }),
})
})
} catch (error) {
if (isAuthError(error)) {
return this._returnResult({ data: null, error })
}

throw error
}
}

/**
* Revokes a user's OAuth grant for a specific client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*/
private async _revokeOAuthGrant(options: {
clientId: string
}): Promise<AuthOAuthRevokeGrantResponse> {
try {
return await this._useSession(async (result) => {
const {
data: { session },
error: sessionError,
} = result

if (sessionError) {
return this._returnResult({ data: null, error: sessionError })
}

if (!session) {
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
}

return await _request(this.fetch, 'DELETE', `${this.url}/user/oauth/grants`, {
headers: this.headers,
jwt: session.access_token,
query: { client_id: options.clientId },
xform: () => ({ data: {}, error: null }),
})
})
} catch (error) {
if (isAuthError(error)) {
return this._returnResult({ data: null, error })
}

throw error
}
}

private async fetchJwk(kid: string, jwks: { keys: JWK[] } = { keys: [] }): Promise<JWK | null> {
// try fetching from the supplied jwks
let jwk = jwks.keys.find((key) => key.kid === kid)
Expand Down
52 changes: 49 additions & 3 deletions packages/core/auth-js/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1630,11 +1630,11 @@ export interface GoTrueAdminOAuthApi {
*/
export type OAuthAuthorizationClient = {
/** Unique identifier for the OAuth client (UUID) */
client_id: string
id: string
/** Human-readable name of the OAuth client */
client_name: string
name: string
/** URI of the OAuth client's website */
client_uri: string
uri: string
/** URI of the OAuth client's logo */
logo_uri: string
}
Expand Down Expand Up @@ -1676,6 +1676,31 @@ export type AuthOAuthConsentResponse = RequestResult<{
redirect_url: string
}>

/**
* An OAuth grant representing a user's authorization of an OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*/
export type OAuthGrant = {
/** OAuth client information */
client: OAuthAuthorizationClient
/** Array of scopes granted to this client */
scopes: string[]
/** Timestamp when the grant was created (ISO 8601 date-time) */
granted_at: string
}

/**
* Response type for listing user's OAuth grants.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*/
export type AuthOAuthGrantsResponse = RequestResult<OAuthGrant[]>

/**
* Response type for revoking an OAuth grant.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*/
export type AuthOAuthRevokeGrantResponse = RequestResult<{}>

/**
* Contains all OAuth 2.1 authorization server user-facing methods.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
Expand Down Expand Up @@ -1722,4 +1747,25 @@ export interface AuthOAuthServerApi {
authorizationId: string,
options?: { skipBrowserRedirect?: boolean }
): Promise<AuthOAuthConsentResponse>

/**
* Lists all OAuth grants that the authenticated user has authorized.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* @returns Response with array of OAuth grants with client information and granted scopes
*/
listGrants(): Promise<AuthOAuthGrantsResponse>

/**
* Revokes a user's OAuth grant for a specific client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* Revocation marks consent as revoked, deletes active sessions for that OAuth client,
* and invalidates associated refresh tokens.
*
* @param options - Revocation options
* @param options.clientId - The OAuth client identifier (UUID) to revoke access for
* @returns Empty response on successful revocation
*/
revokeGrant(options: { clientId: string }): Promise<AuthOAuthRevokeGrantResponse>
}