Skip to content

Commit 74e75ce

Browse files
committed
feat(auth): add OAuth grant listing and revocation endpoints
1 parent 53f9aa2 commit 74e75ce

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

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

Lines changed: 75 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,
@@ -339,6 +341,8 @@ export default class GoTrueClient {
339341
getAuthorizationDetails: this._getAuthorizationDetails.bind(this),
340342
approveAuthorization: this._approveAuthorization.bind(this),
341343
denyAuthorization: this._denyAuthorization.bind(this),
344+
listGrants: this._listOAuthGrants.bind(this),
345+
revokeGrant: this._revokeOAuthGrant.bind(this),
342346
}
343347

344348
if (this.persistSession) {
@@ -3544,6 +3548,77 @@ export default class GoTrueClient {
35443548
}
35453549
}
35463550

3551+
/**
3552+
* Lists all OAuth grants that the authenticated user has authorized.
3553+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3554+
*/
3555+
private async _listOAuthGrants(): Promise<AuthOAuthGrantsResponse> {
3556+
try {
3557+
return await this._useSession(async (result) => {
3558+
const {
3559+
data: { session },
3560+
error: sessionError,
3561+
} = result
3562+
3563+
if (sessionError) {
3564+
return this._returnResult({ data: null, error: sessionError })
3565+
}
3566+
3567+
if (!session) {
3568+
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3569+
}
3570+
3571+
return await _request(this.fetch, 'GET', `${this.url}/user/oauth/grants`, {
3572+
headers: this.headers,
3573+
jwt: session.access_token,
3574+
xform: (data: any) => ({ data, error: null }),
3575+
})
3576+
})
3577+
} catch (error) {
3578+
if (isAuthError(error)) {
3579+
return this._returnResult({ data: null, error })
3580+
}
3581+
3582+
throw error
3583+
}
3584+
}
3585+
3586+
/**
3587+
* Revokes a user's OAuth grant for a specific client.
3588+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3589+
*/
3590+
private async _revokeOAuthGrant(clientId: string): Promise<AuthOAuthRevokeGrantResponse> {
3591+
try {
3592+
return await this._useSession(async (result) => {
3593+
const {
3594+
data: { session },
3595+
error: sessionError,
3596+
} = result
3597+
3598+
if (sessionError) {
3599+
return this._returnResult({ data: null, error: sessionError })
3600+
}
3601+
3602+
if (!session) {
3603+
return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3604+
}
3605+
3606+
return await _request(this.fetch, 'DELETE', `${this.url}/user/oauth/grants`, {
3607+
headers: this.headers,
3608+
jwt: session.access_token,
3609+
query: { client_id: clientId },
3610+
xform: () => ({ data: {}, error: null }),
3611+
})
3612+
})
3613+
} catch (error) {
3614+
if (isAuthError(error)) {
3615+
return this._returnResult({ data: null, error })
3616+
}
3617+
3618+
throw error
3619+
}
3620+
}
3621+
35473622
private async fetchJwk(kid: string, jwks: { keys: JWK[] } = { keys: [] }): Promise<JWK | null> {
35483623
// try fetching from the supplied jwks
35493624
let jwk = jwks.keys.find((key) => key.kid === kid)

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,40 @@ export type AuthOAuthConsentResponse = RequestResult<{
16761676
redirect_url: string
16771677
}>
16781678

1679+
/**
1680+
* An OAuth grant representing a user's authorization of an OAuth client.
1681+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1682+
*/
1683+
export type OAuthGrant = {
1684+
/** OAuth client identifier (UUID) */
1685+
client_id: string
1686+
/** Human-readable name of the OAuth client */
1687+
client_name: string
1688+
/** URI of the OAuth client's website */
1689+
client_uri: string
1690+
/** URI of the OAuth client's logo */
1691+
logo_uri: string
1692+
/** Array of scopes granted to this client */
1693+
scopes: string[]
1694+
/** Timestamp when the grant was created (ISO 8601 date-time) */
1695+
granted_at: string
1696+
}
1697+
1698+
/**
1699+
* Response type for listing user's OAuth grants.
1700+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1701+
*/
1702+
export type AuthOAuthGrantsResponse = RequestResult<{
1703+
/** Array of OAuth grants authorized by the user */
1704+
grants: OAuthGrant[]
1705+
}>
1706+
1707+
/**
1708+
* Response type for revoking an OAuth grant.
1709+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1710+
*/
1711+
export type AuthOAuthRevokeGrantResponse = RequestResult<{}>
1712+
16791713
/**
16801714
* Contains all OAuth 2.1 authorization server user-facing methods.
16811715
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
@@ -1722,4 +1756,24 @@ export interface AuthOAuthServerApi {
17221756
authorizationId: string,
17231757
options?: { skipBrowserRedirect?: boolean }
17241758
): Promise<AuthOAuthConsentResponse>
1759+
1760+
/**
1761+
* Lists all OAuth grants that the authenticated user has authorized.
1762+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1763+
*
1764+
* @returns Array of OAuth grants with client information and granted scopes
1765+
*/
1766+
listGrants(): Promise<AuthOAuthGrantsResponse>
1767+
1768+
/**
1769+
* Revokes a user's OAuth grant for a specific client.
1770+
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
1771+
*
1772+
* Revocation marks consent as revoked, deletes active sessions for that OAuth client,
1773+
* and invalidates associated refresh tokens.
1774+
*
1775+
* @param clientId - The OAuth client identifier (UUID) to revoke access for
1776+
* @returns Empty response on successful revocation
1777+
*/
1778+
revokeGrant(clientId: string): Promise<AuthOAuthRevokeGrantResponse>
17251779
}

0 commit comments

Comments
 (0)