fix: remove oauth apps from self hosted (#43865)

## 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?

self hosted fix: remove oauth apps and add tests so this never happens
again
This commit is contained in:
Ali Waseem
2026-03-17 08:10:23 -06:00
committed by GitHub
parent 3b010f146f
commit 609b6db112
2 changed files with 212 additions and 34 deletions

View File

@@ -0,0 +1,143 @@
import { describe, expect, it } from 'vitest'
import { generateAuthMenu, GenerateAuthMenuOptions } from './AuthLayout.utils'
const allFeaturesEnabled: GenerateAuthMenuOptions = {
ref: 'test-ref',
isPlatform: true,
showOverview: true,
features: {
signInProviders: true,
rateLimits: true,
emails: true,
multiFactor: true,
attackProtection: true,
performance: true,
},
}
const allFeaturesDisabled: GenerateAuthMenuOptions = {
ref: 'test-ref',
isPlatform: true,
showOverview: false,
features: {
signInProviders: false,
rateLimits: false,
emails: false,
multiFactor: false,
attackProtection: false,
performance: false,
},
}
function flatItemNames(menu: ReturnType<typeof generateAuthMenu>): string[] {
return menu.flatMap((group) => group.items.map((item) => item.name))
}
function findItem(menu: ReturnType<typeof generateAuthMenu>, name: string) {
for (const group of menu) {
const item = group.items.find((i) => i.name === name)
if (item) return item
}
return undefined
}
describe('generateAuthMenu', () => {
it('platform with all features enabled includes all menu items', () => {
const menu = generateAuthMenu(allFeaturesEnabled)
const names = flatItemNames(menu)
expect(names).toContain('Overview')
expect(names).toContain('Users')
expect(names).toContain('OAuth Apps')
expect(names).toContain('Email')
expect(names).toContain('Sign In / Providers')
expect(names).toContain('OAuth Server')
expect(names).toContain('Sessions')
expect(names).toContain('Rate Limits')
expect(names).toContain('Multi-Factor')
expect(names).toContain('URL Configuration')
expect(names).toContain('Attack Protection')
expect(names).toContain('Auth Hooks')
expect(names).toContain('Audit Logs')
expect(names).toContain('Performance')
})
it('platform with all features disabled shows only core items', () => {
const menu = generateAuthMenu(allFeaturesDisabled)
const names = flatItemNames(menu)
expect(names).toContain('Users')
expect(names).toContain('OAuth Apps')
expect(names).toContain('Policies')
expect(names).toContain('OAuth Server')
expect(names).toContain('Sessions')
expect(names).toContain('URL Configuration')
expect(names).toContain('Auth Hooks')
expect(names).toContain('Audit Logs')
expect(names).not.toContain('Overview')
expect(names).not.toContain('Email')
expect(names).not.toContain('Sign In / Providers')
expect(names).not.toContain('Rate Limits')
expect(names).not.toContain('Multi-Factor')
expect(names).not.toContain('Attack Protection')
expect(names).not.toContain('Performance')
})
it('self-hosted hides OAuth Apps, Notifications, and platform-only Configuration items', () => {
const menu = generateAuthMenu({
...allFeaturesEnabled,
isPlatform: false,
})
const names = flatItemNames(menu)
const groupTitles = menu.map((g) => g.title)
expect(names).not.toContain('OAuth Apps')
expect(groupTitles).not.toContain('Notifications')
// Configuration should only have Policies
const configGroup = menu.find((g) => g.title === 'Configuration')!
expect(configGroup.items).toHaveLength(1)
expect(configGroup.items[0].name).toBe('Policies')
})
it('shows Overview when showOverview is true', () => {
const menu = generateAuthMenu({ ...allFeaturesDisabled, showOverview: true })
expect(flatItemNames(menu)).toContain('Overview')
})
it('hides Overview when showOverview is false', () => {
const menu = generateAuthMenu({ ...allFeaturesEnabled, showOverview: false })
expect(flatItemNames(menu)).not.toContain('Overview')
})
it.each([
['signInProviders', 'Sign In / Providers'],
['rateLimits', 'Rate Limits'],
['emails', 'Email'],
['multiFactor', 'Multi-Factor'],
['attackProtection', 'Attack Protection'],
['performance', 'Performance'],
] as const)('feature flag %s toggles %s', (flag, itemName) => {
const withEnabled = generateAuthMenu({
...allFeaturesDisabled,
features: { ...allFeaturesDisabled.features, [flag]: true },
})
const withDisabled = generateAuthMenu({
...allFeaturesEnabled,
features: { ...allFeaturesEnabled.features, [flag]: false },
})
expect(flatItemNames(withEnabled)).toContain(itemName)
expect(flatItemNames(withDisabled)).not.toContain(itemName)
})
it('generates correct URLs using the ref parameter', () => {
const menu = generateAuthMenu({ ...allFeaturesEnabled, ref: 'my-project' })
const users = findItem(menu, 'Users')
const oauthApps = findItem(menu, 'OAuth Apps')
expect(users?.url).toBe('/project/my-project/auth/users')
expect(oauthApps?.url).toBe('/project/my-project/auth/oauth-apps')
})
})

View File

@@ -4,50 +4,50 @@ import { IS_PLATFORM } from 'lib/constants'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
const { ref } = useParams()
const authenticationShowOverview = useFlag('authOverviewPage')
const {
authenticationSignInProviders,
authenticationRateLimits,
authenticationEmails,
authenticationMultiFactor,
authenticationAttackProtection,
authenticationPerformance,
} = useIsFeatureEnabled([
'authentication:sign_in_providers',
'authentication:rate_limits',
'authentication:emails',
'authentication:multi_factor',
'authentication:attack_protection',
'authentication:performance',
])
export interface GenerateAuthMenuOptions {
ref?: string
isPlatform: boolean
showOverview: boolean
features: {
signInProviders: boolean
rateLimits: boolean
emails: boolean
multiFactor: boolean
attackProtection: boolean
performance: boolean
}
}
export function generateAuthMenu(options: GenerateAuthMenuOptions): ProductMenuGroup[] {
const { ref, isPlatform, showOverview, features } = options
const baseUrl = `/project/${ref}/auth`
return [
{
title: 'Manage',
items: [
...(authenticationShowOverview
...(showOverview
? [{ name: 'Overview', key: 'overview', url: `${baseUrl}/overview`, items: [] }]
: []),
{ name: 'Users', key: 'users', url: `${baseUrl}/users`, items: [] },
{
name: 'OAuth Apps',
key: 'oauth-apps',
url: `${baseUrl}/oauth-apps`,
items: [],
},
...(isPlatform
? [
{
name: 'OAuth Apps',
key: 'oauth-apps',
url: `${baseUrl}/oauth-apps`,
items: [],
},
]
: []),
],
},
...(authenticationEmails && IS_PLATFORM
...(features.emails && isPlatform
? [
{
title: 'Notifications',
items: [
...(authenticationEmails
...(features.emails
? [
{
name: 'Email',
@@ -71,9 +71,9 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
url: `${baseUrl}/policies`,
items: [],
},
...(IS_PLATFORM
...(isPlatform
? [
...(authenticationSignInProviders
...(features.signInProviders
? [
{
name: 'Sign In / Providers',
@@ -96,7 +96,7 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
url: `${baseUrl}/sessions`,
items: [],
},
...(authenticationRateLimits
...(features.rateLimits
? [
{
name: 'Rate Limits',
@@ -106,7 +106,7 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
},
]
: []),
...(authenticationMultiFactor
...(features.multiFactor
? [
{
name: 'Multi-Factor',
@@ -122,7 +122,7 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
url: `${baseUrl}/url-configuration`,
items: [],
},
...(authenticationAttackProtection
...(features.attackProtection
? [
{
name: 'Attack Protection',
@@ -145,7 +145,7 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
url: `${baseUrl}/audit-logs`,
items: [],
},
...(authenticationPerformance
...(features.performance
? [
{
name: 'Performance',
@@ -161,3 +161,38 @@ export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
},
]
}
export const useGenerateAuthMenu = (): ProductMenuGroup[] => {
const { ref } = useParams()
const showOverview = useFlag('authOverviewPage')
const {
authenticationSignInProviders,
authenticationRateLimits,
authenticationEmails,
authenticationMultiFactor,
authenticationAttackProtection,
authenticationPerformance,
} = useIsFeatureEnabled([
'authentication:sign_in_providers',
'authentication:rate_limits',
'authentication:emails',
'authentication:multi_factor',
'authentication:attack_protection',
'authentication:performance',
])
return generateAuthMenu({
ref,
isPlatform: IS_PLATFORM,
showOverview,
features: {
signInProviders: authenticationSignInProviders,
rateLimits: authenticationRateLimits,
emails: authenticationEmails,
multiFactor: authenticationMultiFactor,
attackProtection: authenticationAttackProtection,
performance: authenticationPerformance,
},
})
}