Skip to content

Commit 31b8fbe

Browse files
joshenlimivasilov
andauthored
Deprecate getAPIKeys from project settings v2, use new api keys endpoint instead (supabase#37300)
* Deprecate getAPIKeys from project settings v2, use new api keys endpoint instead * use getPreferredKeys where appropriate * Prevent usage of secret key for storage and realtime inspector * Add dashboard API api-keys endpoint * Simplify * Disable edge functions test if legacy api keys are disabled * Revert * Fix graphiql * Remove all usage of api keys from project settings, except DisplayApiSettings * Update * Fix * Small fix for an undefined upload url. * Fix the storage state initialization when the resumable url changes. --------- Co-authored-by: Ivan Vasilov <[email protected]>
1 parent e70242f commit 31b8fbe

File tree

25 files changed

+314
-136
lines changed

25 files changed

+314
-136
lines changed

apps/studio/components/interfaces/App/CommandMenu/ApiKeys.tsx

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Key } from 'lucide-react'
22
import { useMemo } from 'react'
33

4-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
5-
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
4+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
5+
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
66
import { Badge, copyToClipboard } from 'ui'
77
import type { ICommand } from 'ui-patterns/CommandMenu'
88
import {
@@ -21,14 +21,11 @@ export function useApiKeysCommands() {
2121
const setIsOpen = useSetCommandMenuOpen()
2222
const setPage = useSetPage()
2323

24-
const project = useSelectedProject()
24+
const { data: project } = useSelectedProjectQuery()
2525
const ref = project?.ref || '_'
2626

27-
const { data: settings } = useProjectSettingsV2Query(
28-
{ projectRef: project?.ref },
29-
{ enabled: !!project }
30-
)
31-
const { anonKey, serviceKey } = getAPIKeys(settings)
27+
const { data: apiKeys } = useAPIKeysQuery({ projectRef: project?.ref, reveal: true })
28+
const { anonKey, serviceKey, publishableKey, allSecretKeys } = getKeys(apiKeys)
3229

3330
const commands = useMemo(
3431
() =>
@@ -42,9 +39,10 @@ export function useApiKeysCommands() {
4239
setIsOpen(false)
4340
},
4441
badge: () => (
45-
<span className="flex items-center gap-2">
42+
<span className="flex items-center gap-x-1">
4643
<Badge>Project: {project?.name}</Badge>
4744
<Badge>Public</Badge>
45+
<Badge className="capitalize">{anonKey.type}</Badge>
4846
</span>
4947
),
5048
icon: () => <Key />,
@@ -58,13 +56,47 @@ export function useApiKeysCommands() {
5856
setIsOpen(false)
5957
},
6058
badge: () => (
61-
<span className="flex items-center gap-2">
59+
<span className="flex items-center gap-x-1">
6260
<Badge>Project: {project?.name}</Badge>
6361
<Badge variant="destructive">Secret</Badge>
62+
<Badge className="capitalize">{serviceKey.type}</Badge>
63+
</span>
64+
),
65+
icon: () => <Key />,
66+
},
67+
project &&
68+
publishableKey && {
69+
id: 'publishable-key',
70+
name: `Copy publishable key`,
71+
action: () => {
72+
copyToClipboard(publishableKey.api_key ?? '')
73+
setIsOpen(false)
74+
},
75+
badge: () => (
76+
<span className="flex items-center gap-x-1">
77+
<Badge>Project: {project?.name}</Badge>
78+
<Badge className="capitalize">{publishableKey.type}</Badge>
6479
</span>
6580
),
6681
icon: () => <Key />,
6782
},
83+
...(project && allSecretKeys
84+
? allSecretKeys.map((key) => ({
85+
id: key.id,
86+
name: `Copy secret key (${key.name})`,
87+
action: () => {
88+
copyToClipboard(key.api_key ?? '')
89+
setIsOpen(false)
90+
},
91+
badge: () => (
92+
<span className="flex items-center gap-x-1">
93+
<Badge>Project: {project?.name}</Badge>
94+
<Badge className="capitalize">{key.type}</Badge>
95+
</span>
96+
),
97+
icon: () => <Key />,
98+
}))
99+
: []),
68100
!(anonKey || serviceKey) && {
69101
id: 'api-keys-project-settings',
70102
name: 'See API keys in Project Settings',

apps/studio/components/interfaces/Connect/Connect.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { useMemo, useState } from 'react'
77
import { DatabaseConnectionString } from 'components/interfaces/Connect/DatabaseConnectionString'
88
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
99
import Panel from 'components/ui/Panel'
10-
import { useAPIKeysQuery } from 'data/api-keys/api-keys-query'
11-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
10+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
11+
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
1212
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1313
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
1414
import { PROJECT_STATUS } from 'lib/constants'
@@ -138,21 +138,28 @@ export const Connect = () => {
138138
return []
139139
}
140140

141-
const { anonKey } = canReadAPIKeys ? getAPIKeys(settings) : { anonKey: null }
142-
const { data: apiKeys } = useAPIKeysQuery({ projectRef, reveal: false })
141+
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
142+
const { anonKey, publishableKey } = canReadAPIKeys
143+
? getKeys(apiKeys)
144+
: { anonKey: null, publishableKey: null }
143145

144146
const projectKeys = useMemo(() => {
145147
const protocol = settings?.app_config?.protocol ?? 'https'
146148
const endpoint = settings?.app_config?.endpoint ?? ''
147149
const apiHost = canReadAPIKeys ? `${protocol}://${endpoint ?? '-'}` : ''
148150

149-
const apiUrl = canReadAPIKeys ? apiHost : null
150151
return {
151152
apiUrl: apiHost ?? null,
152153
anonKey: anonKey?.api_key ?? null,
153-
publishableKey: apiKeys?.find(({ type }) => type === 'publishable')?.api_key ?? null,
154+
publishableKey: publishableKey?.api_key ?? null,
154155
}
155-
}, [apiKeys, anonKey, canReadAPIKeys, settings])
156+
}, [
157+
settings?.app_config?.protocol,
158+
settings?.app_config?.endpoint,
159+
canReadAPIKeys,
160+
anonKey?.api_key,
161+
publishableKey?.api_key,
162+
])
156163

157164
const filePath = getContentFilePath({
158165
connectionObject,

apps/studio/components/interfaces/Database/Hooks/FormContents.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ export const FormContents = ({
227227
errors={errors}
228228
httpHeaders={httpHeaders}
229229
httpParameters={httpParameters}
230-
onAddHeader={(header?: any) => {
231-
if (header) setHttpHeaders(httpHeaders.concat(header))
230+
onAddHeaders={(headers?: any[]) => {
231+
if (headers) setHttpHeaders(httpHeaders.concat(headers))
232232
else setHttpHeaders(httpHeaders.concat({ id: uuidv4(), name: '', value: '' }))
233233
}}
234234
onUpdateHeader={(idx, property, value) =>

apps/studio/components/interfaces/Database/Hooks/HTTPRequestFields.tsx

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useParams } from 'common'
55
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
66
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
77
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
8-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
8+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
99
import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
1010
import { uuidv4 } from 'lib/helpers'
1111
import {
@@ -27,7 +27,7 @@ interface HTTPRequestFieldsProps {
2727
errors: any
2828
httpHeaders: HTTPArgument[]
2929
httpParameters: HTTPArgument[]
30-
onAddHeader: (header?: any) => void
30+
onAddHeaders: (headers?: any[]) => void
3131
onUpdateHeader: (idx: number, property: string, value: string) => void
3232
onRemoveHeader: (idx: number) => void
3333
onAddParameter: () => void
@@ -40,21 +40,22 @@ const HTTPRequestFields = ({
4040
errors,
4141
httpHeaders = [],
4242
httpParameters = [],
43-
onAddHeader,
43+
onAddHeaders,
4444
onUpdateHeader,
4545
onRemoveHeader,
4646
onAddParameter,
4747
onUpdateParameter,
4848
onRemoveParameter,
4949
}: HTTPRequestFieldsProps) => {
50-
const { project: selectedProject } = useProjectContext()
5150
const { ref } = useParams()
52-
const { data: settings } = useProjectSettingsV2Query({ projectRef: ref })
51+
const { project: selectedProject } = useProjectContext()
52+
5353
const { data: functions } = useEdgeFunctionsQuery({ projectRef: ref })
54+
const { data: apiKeys } = useAPIKeysQuery({ projectRef: ref, reveal: true })
5455

5556
const edgeFunctions = functions ?? []
56-
const { serviceKey } = getAPIKeys(settings)
57-
const apiKey = serviceKey?.api_key ?? '[YOUR API KEY]'
57+
const { serviceKey, secretKey } = getKeys(apiKeys)
58+
const apiKey = secretKey?.api_key ?? serviceKey?.api_key ?? '[YOUR API KEY]'
5859

5960
return (
6061
<>
@@ -159,30 +160,37 @@ const HTTPRequestFields = ({
159160
size="tiny"
160161
icon={<Plus />}
161162
className={cn(type === 'supabase_function' && 'rounded-r-none px-3')}
162-
onClick={onAddHeader}
163+
onClick={() => onAddHeaders()}
163164
>
164165
Add a new header
165166
</Button>
166167
{type === 'supabase_function' && (
167168
<DropdownMenu>
168169
<DropdownMenuTrigger asChild>
169-
<Button type="default" className="rounded-l-none px-[4px] py-[5px]">
170-
<ChevronDown />
171-
</Button>
170+
<Button
171+
type="default"
172+
icon={<ChevronDown />}
173+
className="rounded-l-none px-[4px] py-[5px]"
174+
/>
172175
</DropdownMenuTrigger>
173176
<DropdownMenuContent align="end" side="bottom">
174177
<DropdownMenuItem
175178
key="add-auth-header"
176-
onClick={() =>
177-
onAddHeader({
178-
id: uuidv4(),
179-
name: 'Authorization',
180-
value: `Bearer ${apiKey}`,
181-
})
182-
}
179+
onClick={() => {
180+
onAddHeaders([
181+
{
182+
id: uuidv4(),
183+
name: 'Authorization',
184+
value: `Bearer ${apiKey}`,
185+
},
186+
...(serviceKey?.type === 'secret'
187+
? [{ id: uuidv4(), name: 'apikey', value: apiKey }]
188+
: []),
189+
])
190+
}}
183191
>
184192
<div className="space-y-1">
185-
<p className="block text-foreground">Add auth header with service key</p>
193+
<p className="block text-foreground">Add auth header with secret key</p>
186194
<p className="text-foreground-light">
187195
Required if your edge function enforces JWT verification
188196
</p>
@@ -192,11 +200,13 @@ const HTTPRequestFields = ({
192200
<DropdownMenuItem
193201
key="add-source-header"
194202
onClick={() =>
195-
onAddHeader({
196-
id: uuidv4(),
197-
name: 'x-supabase-webhook-source',
198-
value: `[Use a secret value]`,
199-
})
203+
onAddHeaders([
204+
{
205+
id: uuidv4(),
206+
name: 'x-supabase-webhook-source',
207+
value: `[Use a secret value]`,
208+
},
209+
])
200210
}
201211
>
202212
<div className="space-y-1">

apps/studio/components/interfaces/Docs/Authentication.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import Link from 'next/link'
22

33
import { useParams } from 'common'
4-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
4+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
5+
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
56
import CodeSnippet from './CodeSnippet'
67
import Snippets from './Snippets'
78

@@ -12,9 +13,10 @@ interface AuthenticationProps {
1213

1314
const Authentication = ({ selectedLang, showApiKey }: AuthenticationProps) => {
1415
const { ref: projectRef } = useParams()
16+
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
1517
const { data: settings } = useProjectSettingsV2Query({ projectRef })
1618

17-
const { anonKey, serviceKey } = getAPIKeys(settings)
19+
const { anonKey, serviceKey } = getKeys(apiKeys)
1820
const protocol = settings?.app_config?.protocol ?? 'https'
1921
const hostEndpoint = settings?.app_config?.endpoint
2022
const endpoint = `${protocol}://${hostEndpoint ?? ''}`

apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const INVOCATION_TABS: InvocationTab[] = [
1212
label: 'cURL',
1313
language: 'bash',
1414
code: (functionUrl, _, apiKey) => `curl -L -X POST '${functionUrl}' \\
15-
-H 'Authorization: Bearer ${apiKey}' \\
15+
-H 'Authorization: Bearer ${apiKey}' \\ ${apiKey.includes('publishable') ? `\n -H 'apikey: ${apiKey}' \\` : ''}
1616
-H 'Content-Type: application/json' \\
1717
--data '{"name":"Functions"}'`,
1818
},

apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import z from 'zod'
1212
import { useParams } from 'common'
1313
import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
1414
import AlertError from 'components/ui/AlertError'
15-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
15+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
16+
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
1617
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
1718
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
1819
import { useEdgeFunctionDeleteMutation } from 'data/edge-functions/edge-functions-delete-mutation'
@@ -60,6 +61,7 @@ export const EdgeFunctionDetails = () => {
6061
const [showDeleteModal, setShowDeleteModal] = useState(false)
6162
const canUpdateEdgeFunction = useCheckPermissions(PermissionAction.FUNCTIONS_WRITE, '*')
6263

64+
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
6365
const { data: settings } = useProjectSettingsV2Query({ projectRef })
6466
const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
6567
const {
@@ -86,8 +88,8 @@ export const EdgeFunctionDetails = () => {
8688
defaultValues: { name: '', verify_jwt: false },
8789
})
8890

89-
const { anonKey } = getAPIKeys(settings)
90-
const apiKey = anonKey?.api_key ?? '[YOUR ANON KEY]'
91+
const { anonKey, publishableKey } = getKeys(apiKeys)
92+
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
9193

9294
const protocol = settings?.app_config?.protocol ?? 'https'
9395
const endpoint = settings?.app_config?.endpoint ?? ''

apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import * as z from 'zod'
66

77
import { useParams } from 'common'
88
import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector'
9+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
910
import { useSessionAccessTokenQuery } from 'data/auth/session-access-token-query'
1011
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
11-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
12+
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
1213
import { useEdgeFunctionTestMutation } from 'data/edge-functions/edge-function-test-mutation'
1314
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
1415
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
@@ -86,10 +87,11 @@ export const EdgeFunctionTesterSheet = ({ visible, onClose }: EdgeFunctionTester
8687
const [response, setResponse] = useState<ResponseData | null>(null)
8788
const [error, setError] = useState<string | null>(null)
8889

90+
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
8991
const { data: config } = useProjectPostgrestConfigQuery({ projectRef })
9092
const { data: settings } = useProjectSettingsV2Query({ projectRef })
9193
const { data: accessToken } = useSessionAccessTokenQuery({ enabled: IS_PLATFORM })
92-
const { serviceKey } = getAPIKeys(settings)
94+
const { serviceKey } = getKeys(apiKeys)
9395

9496
const { mutate: sendEvent } = useSendEventMutation()
9597
const { mutate: testEdgeFunction, isLoading } = useEdgeFunctionTestMutation({

apps/studio/components/interfaces/Functions/TerminalInstructions.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { useParams } from 'common'
66
import CommandRender from 'components/interfaces/Functions/CommandRender'
77
import { DocsButton } from 'components/ui/DocsButton'
88
import { useAccessTokensQuery } from 'data/access-tokens/access-tokens-query'
9-
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
9+
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
10+
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
1011
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
1112
import {
1213
Button,
@@ -30,11 +31,12 @@ export const TerminalInstructions = forwardRef<
3031
const [showInstructions, setShowInstructions] = useState(!closable)
3132

3233
const { data: tokens } = useAccessTokensQuery()
34+
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
3335
const { data: settings } = useProjectSettingsV2Query({ projectRef })
3436
const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
3537

36-
const { anonKey } = getAPIKeys(settings)
37-
const apiKey = anonKey?.api_key ?? '[YOUR ANON KEY]'
38+
const { anonKey, publishableKey } = getKeys(apiKeys)
39+
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
3840

3941
const protocol = settings?.app_config?.protocol ?? 'https'
4042
const endpoint = settings?.app_config?.endpoint ?? ''
@@ -74,15 +76,14 @@ export const TerminalInstructions = forwardRef<
7476
comment: 'Deploy your function',
7577
},
7678
{
77-
command: `curl -L -X POST 'https://${projectRef}.supabase.${restUrlTld}/functions/v1/hello-world' -H 'Authorization: Bearer ${
78-
apiKey ?? '[YOUR ANON KEY]'
79-
}' --data '{"name":"Functions"}'`,
79+
command: `curl -L -X POST 'https://${projectRef}.supabase.${restUrlTld}/functions/v1/hello-world' -H 'Authorization: Bearer ${apiKey}'${anonKey?.type === 'publishable' ? ` -H 'apikey: ${apiKey}'` : ''} --data '{"name":"Functions"}'`,
8080
description: 'Invokes the hello-world function',
8181
jsx: () => {
8282
return (
8383
<>
8484
<span className="text-brand-600">curl</span> -L -X POST '{functionsEndpoint}
85-
/hello-world' -H 'Authorization: Bearer [YOUR ANON KEY]'{' '}
85+
/hello-world' -H 'Authorization: Bearer [YOUR ANON KEY]' s
86+
{anonKey?.type === 'publishable' ? " -H 'apikey: [YOUR ANON KEY]' " : ''}
8687
{`--data '{"name":"Functions"}'`}
8788
</>
8889
)

0 commit comments

Comments
 (0)