chore: handle encoding due to bug in deleting and adding encodings (#46562)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Call API manually due to minor bug in Supabase js right now. 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Refactored OAuth custom provider management operations to use a new
admin request helper for update and delete operations, replacing direct
client calls.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ali Waseem authored and GitHub committed 2026-06-01 12:04:23 -06:00
1 parent bc6a459fd2
commit 4429647db7
3 files changed
+97 -15

No files matched your search

@@ -0,0 +1,83 @@
import { getOrRefreshTemporaryApiKey } from '@/data/api-keys/temp-api-keys-utils'
import { handleError } from '@/data/fetchers'
/**
* Keeps the structural `custom:` prefix literal while still encoding the
* user-controlled slug, e.g. `custom:naver` -> `custom:naver`,
* `custom:my provider` -> `custom:my%20provider`.
*/
function encodeCustomProviderIdentifier(identifier: string) {
const slug = identifier.replace(/^custom:/i, '')
return `custom:${encodeURIComponent(slug)}`
}
function extractErrorMessage(payload: unknown, fallback: string) {
if (payload && typeof payload === 'object') {
if ('msg' in payload && typeof payload.msg === 'string') return payload.msg
if ('message' in payload && typeof payload.message === 'string') return payload.message
if ('error_description' in payload && typeof payload.error_description === 'string') {
return payload.error_description
}
}
return fallback
}
/**
* Performs a raw request against the GoTrue custom-providers admin endpoint.
*
* We bypass the auth-js SDK's `customProviders.updateProvider` / `deleteProvider`
* helpers here because they run the identifier through `encodeURIComponent`
* (supabase-js#2383), which turns the required `custom:` prefix into `custom%3A`.
* The GoTrue endpoint does not decode the path segment, so the encoded colon
* fails its `identifier must start with 'custom:'` validation.
*
* TODO: remove this and return to the SDK helpers once GoTrue decodes the
* custom-provider path segment (supabase-js#2383).
*/
export async function customProviderAdminRequest({
method,
projectRef,
clientEndpoint,
identifier,
body,
}: {
method: 'PUT' | 'DELETE'
projectRef: string
clientEndpoint: string
identifier: string
body?: unknown
}) {
const { apiKey } = await getOrRefreshTemporaryApiKey(projectRef)
const path = encodeCustomProviderIdentifier(identifier)
const response = await fetch(`${clientEndpoint}/auth/v1/admin/custom-providers/${path}`, {
method,
headers: {
apikey: apiKey,
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
})
if (!response.ok) {
let payload: unknown
try {
payload = await response.json()
} catch {
// response had no JSON body
}
handleError({
message: extractErrorMessage(payload, `Request failed with status ${response.status}`),
code: response.status,
})
}
if (method === 'DELETE') return null
try {
return await response.json()
} catch {
return null
}
}
@@ -1,9 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { customProviderAdminRequest } from './custom-provider-admin-request'
import { oAuthCustomProvidersKeys } from './keys'
import { handleError } from '@/data/fetchers'
import { createProjectSupabaseClient } from '@/lib/project-supabase-client'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type OAuthCustomProviderDeleteVariables = {
@@ -21,10 +20,12 @@ export async function deleteOAuthCustomProvider({
if (!clientEndpoint) throw new Error('Client endpoint is required')
if (!identifier) throw new Error('Provider identifier is required')
const supabaseClient = await createProjectSupabaseClient(projectRef, clientEndpoint)
const { error } = await supabaseClient.auth.admin.customProviders.deleteProvider(identifier)
if (error) handleError(error)
await customProviderAdminRequest({
method: 'DELETE',
projectRef,
clientEndpoint,
identifier,
})
return null
}
@@ -2,9 +2,8 @@ import type { UpdateCustomProviderParams } from '@supabase/auth-js'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { customProviderAdminRequest } from './custom-provider-admin-request'
import { oAuthCustomProvidersKeys } from './keys'
import { handleError } from '@/data/fetchers'
import { createProjectSupabaseClient } from '@/lib/project-supabase-client'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type OAuthCustomProviderUpdateVariables = UpdateCustomProviderParams & {
@@ -23,14 +22,13 @@ export async function updateOAuthCustomProvider({
if (!clientEndpoint) throw new Error('Client endpoint is required')
if (!identifier) throw new Error('Provider identifier is required')
const supabaseClient = await createProjectSupabaseClient(projectRef, clientEndpoint)
const { data, error } = await supabaseClient.auth.admin.customProviders.updateProvider(
return customProviderAdminRequest({
method: 'PUT',
projectRef,
clientEndpoint,
identifier,
params
)
if (error) handleError(error)
return data!
body: params,
})
}
type OAuthCustomProviderUpdateData = Awaited<ReturnType<typeof updateOAuthCustomProvider>>