Skip to content

Commit d30607e

Browse files
authored
feat(auth): add OAuth grant listing and revocation endpoints (#1833)
1 parent 91cfd78 commit d30607e

File tree

2 files changed

+126
-3
lines changed

2 files changed

+126
-3
lines changed

packages/core/auth-js/src/GoTrueClient.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ import type {
109109
AuthOAuthServerApi,
110110
AuthOAuthAuthorizationDetailsResponse,
111111
AuthOAuthConsentResponse,
112+
AuthOAuthGrantsResponse,
113+
AuthOAuthRevokeGrantResponse,
112114
Prettify,
113115
Provider,
114116
ResendParams,
@@ -352,6 +354,8 @@ export default class GoTrueClient {
352354
getAuthorizationDetails: this._getAuthorizationDetails.bind(this),
353355
approveAuthorization: this._approveAuthorization.bind(this),
354356
denyAuthorization: this._denyAuthorization.bind(this),
357+
listGrants: this._listOAuthGrants.bind(this),
358+
revokeGrant: this._revokeOAuthGrant.bind(this),
355359
}
356360

357361
if (this.persistSession) {
@@ -3567,6 +3571,79 @@ export default class GoTrueClient {
35673571
}
35683572
}
35693573

3574+
/**
3575+
* Lists all OAuth grants that the authenticated user has authorized.
3576+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3577+
*/
3578+
private async _listOAuthGrants(): Promise<AuthOAuthGrantsResponse> {
3579+
try {
3580+
return await this._useSession(async (result) => {
3581+
const {
3582+
data: { session },
3583+
error: sessionError,
3584+
} = result
3585+
3586+
if (sessionError) {
3587+
return this._returnResult({ data: null, error: sessionError })
3588+
}
3589+
3590+
if (!session) {
3591+
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3592+
}
3593+
3594+
return await _request(this.fetch, 'GET', `${this.url}/user/oauth/grants`, {
3595+
headers: this.headers,
3596+
jwt: session.access_token,
3597+
xform: (data: any) => ({ data, error: null }),
3598+
})
3599+
})
3600+
} catch (error) {
3601+
if (isAuthError(error)) {
3602+
return this._returnResult({ data: null, error })
3603+
}
3604+
3605+
throw error
3606+
}
3607+
}
3608+
3609+
/**
3610+
* Revokes a user's OAuth grant for a specific client.
3611+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3612+
*/
3613+
private async _revokeOAuthGrant(options: {
3614+
clientId: string
3615+
}): Promise<AuthOAuthRevokeGrantResponse> {
3616+
try {
3617+
return await this._useSession(async (result) => {
3618+
const {
3619+
data: { session },
3620+
error: sessionError,
3621+
} = result
3622+
3623+
if (sessionError) {
3624+
return this._returnResult({ data: null, error: sessionError })
3625+
}
3626+
3627+
if (!session) {
3628+
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3629+
}
3630+
3631+
return await _request(this.fetch, 'DELETE', `${this.url}/user/oauth/grants`, {
3632+
headers: this.headers,
3633+
jwt: session.access_token,
3634+
query: { client_id: options.clientId },
3635+
xform: () => ({ data: {}, error: null }),
3636+
})
3637+
})
3638+
} catch (error) {
3639+
if (isAuthError(error)) {
3640+
return this._returnResult({ data: null, error })
3641+
}
3642+
3643+
throw error
3644+
}
3645+
}
3646+
35703647
private async fetchJwk(kid: string, jwks: { keys: JWK[] } = { keys: [] }): Promise<JWK | null> {
35713648
// try fetching from the supplied jwks
35723649
let jwk = jwks.keys.find((key) => key.kid === kid)

packages/core/auth-js/src/lib/types.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,11 +1675,11 @@ export interface GoTrueAdminOAuthApi {
16751675
*/
16761676
export type OAuthAuthorizationClient = {
16771677
/** Unique identifier for the OAuth client (UUID) */
1678-
client_id: string
1678+
id: string
16791679
/** Human-readable name of the OAuth client */
1680-
client_name: string
1680+
name: string
16811681
/** URI of the OAuth client's website */
1682-
client_uri: string
1682+
uri: string
16831683
/** URI of the OAuth client's logo */
16841684
logo_uri: string
16851685
}
@@ -1721,6 +1721,31 @@ export type AuthOAuthConsentResponse = RequestResult<{
17211721
redirect_url: string
17221722
}>
17231723

1724+
/**
1725+
* An OAuth grant representing a user's authorization of an OAuth client.
1726+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1727+
*/
1728+
export type OAuthGrant = {
1729+
/** OAuth client information */
1730+
client: OAuthAuthorizationClient
1731+
/** Array of scopes granted to this client */
1732+
scopes: string[]
1733+
/** Timestamp when the grant was created (ISO 8601 date-time) */
1734+
granted_at: string
1735+
}
1736+
1737+
/**
1738+
* Response type for listing user's OAuth grants.
1739+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1740+
*/
1741+
export type AuthOAuthGrantsResponse = RequestResult<OAuthGrant[]>
1742+
1743+
/**
1744+
* Response type for revoking an OAuth grant.
1745+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1746+
*/
1747+
export type AuthOAuthRevokeGrantResponse = RequestResult<{}>
1748+
17241749
/**
17251750
* Contains all OAuth 2.1 authorization server user-facing methods.
17261751
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
@@ -1767,4 +1792,25 @@ export interface AuthOAuthServerApi {
17671792
authorizationId: string,
17681793
options?: { skipBrowserRedirect?: boolean }
17691794
): Promise<AuthOAuthConsentResponse>
1795+
1796+
/**
1797+
* Lists all OAuth grants that the authenticated user has authorized.
1798+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1799+
*
1800+
* @returns Response with array of OAuth grants with client information and granted scopes
1801+
*/
1802+
listGrants(): Promise<AuthOAuthGrantsResponse>
1803+
1804+
/**
1805+
* Revokes a user's OAuth grant for a specific client.
1806+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1807+
*
1808+
* Revocation marks consent as revoked, deletes active sessions for that OAuth client,
1809+
* and invalidates associated refresh tokens.
1810+
*
1811+
* @param options - Revocation options
1812+
* @param options.clientId - The OAuth client identifier (UUID) to revoke access for
1813+
* @returns Empty response on successful revocation
1814+
*/
1815+
revokeGrant(options: { clientId: string }): Promise<AuthOAuthRevokeGrantResponse>
17701816
}

0 commit comments

Comments
 (0)