Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,7 @@ export const security: NavMenuConstant = {
{ name: 'Platform configuration', url: '/guides/security/platform-security' },
{ name: 'Product configuration', url: '/guides/security/product-security' },
{ name: 'Security testing', url: '/guides/security/security-testing' },
{ name: 'Platform Audit Logs', url: '/guides/security/platform-audit-logs' },
],
},
{
Expand Down
45 changes: 45 additions & 0 deletions apps/docs/content/guides/security/platform-audit-logs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: 'Platform Audit Logs'
description: 'Monitor and track organization member activities via platform API or dashboard.'
---

Any [Platform API](/docs/reference/api/introduction) or [dashboard](/dashboard) actions performed by organization members are logged automatically for auditing and security purposes. This includes actions such as creating a new project, inviting members, modifying an edge function or changing project settings.

Besides Platform Audit Logs, Supabase Auth also provides [Auth Audit Logs](/docs/guides/auth/audit-logs) to monitor authentication-related activities within your projects.

<Admonition type="note">

Platform Audit Logs are only available on the [Team and Enterprise plans](/pricing).

</Admonition>

## Accessing audit logs

Platform Audit Logs can be found under your [organization's audit logs](/dashboard/org/_/audit).

<Image
alt="Platform audit logs"
src={{
light: '/docs/img/guides/security/platform-audit-logs--light.png',
dark: '/docs/img/guides/security/platform-audit-logs--dark.png',
}}
zoomable
/>

For each audit log, you can see additional details by clicking on the log entry:

- Timestamp of action
- Actor who performed the action
- IP address
- Email
- Token Type
- Action performed
- Name
- Metadata such as route and response status
- Action Target (Project, organization, Edge Function, ...)

## Limitations

- There is currently no way to export the logs via dashboard
- There is currently no way to set up a log drain of platform audit logs
- Retention periods depend on your plan
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { toast } from 'sonner'
import * as z from 'zod'

import { useParams } from 'common'
import Panel from 'components/ui/Panel'
import { useProjectEndpointQuery } from 'data/config/project-endpoint-query'
import { useOAuthServerAppCreateMutation } from 'data/oauth-server-apps/oauth-server-app-create-mutation'
import { useOAuthServerAppRegenerateSecretMutation } from 'data/oauth-server-apps/oauth-server-app-regenerate-secret-mutation'
import { useOAuthServerAppUpdateMutation } from 'data/oauth-server-apps/oauth-server-app-update-mutation'
import { useSupabaseClientQuery } from 'hooks/use-supabase-client-query'
import {
Button,
FormControl_Shadcn_,
Expand All @@ -37,10 +38,9 @@ import {
Switch,
cn,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { Input } from 'ui-patterns/DataInputs/Input'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import Panel from 'components/ui/Panel'

interface CreateOrUpdateOAuthAppSheetProps {
visible: boolean
Expand Down Expand Up @@ -109,26 +109,21 @@ export const CreateOrUpdateOAuthAppSheet = ({
control: form.control,
})

const { data: supabaseClientData } = useSupabaseClientQuery({ projectRef })
const { mutateAsync: createOAuthApp, isPending: isCreating } = useOAuthServerAppCreateMutation({
const { data: endpointData } = useProjectEndpointQuery({ projectRef })

const { mutate: createOAuthApp, isPending: isCreating } = useOAuthServerAppCreateMutation({
onSuccess: (data) => {
toast.success(`Successfully created OAuth app "${data.client_name}"`)
onSuccess(data)
},
onError: (error) => {
toast.error(error.message)
},
})
const { mutateAsync: updateOAuthApp, isPending: isUpdating } = useOAuthServerAppUpdateMutation({
const { mutate: updateOAuthApp, isPending: isUpdating } = useOAuthServerAppUpdateMutation({
onSuccess: (data) => {
toast.success(`Successfully updated OAuth app "${data.client_name}"`)
onSuccess(data)
},
onError: (error) => {
toast.error(error.message)
},
})
const { mutateAsync: regenerateSecret, isPending: isRegenerating } =
const { mutate: regenerateSecret, isPending: isRegenerating } =
useOAuthServerAppRegenerateSecretMutation({
onSuccess: (data) => {
if (data) {
Expand All @@ -137,9 +132,6 @@ export const CreateOrUpdateOAuthAppSheet = ({
setShowRegenerateDialog(false)
}
},
onError: (error) => {
toast.error(error.message)
},
})

useEffect(() => {
Expand Down Expand Up @@ -207,8 +199,8 @@ export const CreateOrUpdateOAuthAppSheet = ({

updateOAuthApp({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
clientId: appToEdit.client_id,
clientEndpoint: endpointData?.endpoint,
...payload,
})
} else {
Expand All @@ -222,7 +214,7 @@ export const CreateOrUpdateOAuthAppSheet = ({

createOAuthApp({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
clientEndpoint: endpointData?.endpoint,
...payload,
})
}
Expand All @@ -238,13 +230,11 @@ export const CreateOrUpdateOAuthAppSheet = ({
}

const handleConfirmRegenerate = () => {
if (appToEdit?.client_id) {
regenerateSecret({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
clientId: appToEdit.client_id,
})
}
regenerateSecret({
projectRef,
clientId: appToEdit?.client_id,
clientEndpoint: endpointData?.endpoint,
})
}

const handleUploadLogo = () => uploadButtonRef.current?.click()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { OAuthClient } from '@supabase/supabase-js'
import { useParams } from 'common'
import type { OAuthServerAppDeleteVariables } from 'data/oauth-server-apps/oauth-server-app-delete-mutation'
import { useSupabaseClientQuery } from 'hooks/use-supabase-client-query'

import { useProjectEndpointQuery } from 'data/config/project-endpoint-query'
import type { OAuthServerAppDeleteVariables } from 'data/oauth-server-apps/oauth-server-app-delete-mutation'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'

interface DeleteOAuthAppModalProps {
Expand All @@ -22,15 +22,12 @@ export const DeleteOAuthAppModal = ({
}: DeleteOAuthAppModalProps) => {
const { ref: projectRef } = useParams()

const { data: supabaseClientData } = useSupabaseClientQuery({ projectRef })

const { data: endpointData } = useProjectEndpointQuery({ projectRef })
const onConfirmDeleteApp = () => {
if (!selectedApp) return

onDelete({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
clientId: selectedApp.client_id,
clientId: selectedApp?.client_id,
clientEndpoint: endpointData?.endpoint,
})
}

Expand Down
37 changes: 15 additions & 22 deletions apps/studio/components/interfaces/Auth/OAuthApps/OAuthAppsList.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { OAuthClient } from '@supabase/supabase-js'
import { MoreVertical, Edit, Plus, RotateCw, Search, Trash, X } from 'lucide-react'
import { Edit, MoreVertical, Plus, RotateCw, Search, Trash, X } from 'lucide-react'
import Link from 'next/link'
import { useRef, useState } from 'react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useRef, useState } from 'react'
import { toast } from 'sonner'

import { useParams } from 'common'
Expand All @@ -11,11 +11,11 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { FilterPopover } from 'components/ui/FilterPopover'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useProjectEndpointQuery } from 'data/config/project-endpoint-query'
import { useOAuthServerAppDeleteMutation } from 'data/oauth-server-apps/oauth-server-app-delete-mutation'
import { useOAuthServerAppRegenerateSecretMutation } from 'data/oauth-server-apps/oauth-server-app-regenerate-secret-mutation'
import { useOAuthServerAppsQuery } from 'data/oauth-server-apps/oauth-server-apps-query'
import { useSupabaseClientQuery } from 'hooks/use-supabase-client-query'
import { useQueryStateWithSelect, handleErrorOnDelete } from 'hooks/misc/useQueryStateWithSelect'
import { handleErrorOnDelete, useQueryStateWithSelect } from 'hooks/misc/useQueryStateWithSelect'
import {
Badge,
Button,
Expand Down Expand Up @@ -55,12 +55,8 @@ export const OAuthAppsList = () => {
const [filteredClientTypes, setFilteredClientTypes] = useState<string[]>([])
const deletingOAuthAppIdRef = useRef<string | null>(null)

const { data: supabaseClientData } = useSupabaseClientQuery({ projectRef })
const { data, isLoading, isError, error } = useOAuthServerAppsQuery(
{
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
},
{ projectRef },
{ enabled: isOAuthServerEnabled }
)

Expand All @@ -73,6 +69,8 @@ export const OAuthAppsList = () => {
},
})

const { data: endpointData } = useProjectEndpointQuery({ projectRef })

const oAuthApps = data?.clients || []

const [showCreateSheet, setShowCreateSheet] = useQueryState(
Expand Down Expand Up @@ -108,9 +106,6 @@ export const OAuthAppsList = () => {
},
})

const handleEditClick = (app: OAuthClient) => setSelectedAppToEdit(app.client_id)
const handleDeleteClick = (app: OAuthClient) => setSelectedAppToDelete(app.client_id)

const [filterString, setFilterString] = useState<string>('')

const filteredOAuthApps = filterOAuthApps({
Expand Down Expand Up @@ -259,7 +254,7 @@ export const OAuthAppsList = () => {
<Button
type="text"
className="text-link-table-cell text-sm p-0 hover:bg-transparent title [&>span]:!w-full"
onClick={() => handleEditClick(app)}
onClick={() => setSelectedAppToEdit(app.client_id)}
title={app.client_name}
>
{app.client_name}
Expand Down Expand Up @@ -287,7 +282,7 @@ export const OAuthAppsList = () => {
<DropdownMenuItem
className="space-x-2"
onClick={() => {
handleEditClick(app)
setSelectedAppToEdit(app.client_id)
}}
>
<Edit size={12} />
Expand All @@ -307,7 +302,7 @@ export const OAuthAppsList = () => {
)}
<DropdownMenuItem
className="space-x-2"
onClick={() => handleDeleteClick(app)}
onClick={() => setSelectedAppToDelete(app.client_id)}
>
<Trash size={12} />
<p>Delete</p>
Expand Down Expand Up @@ -363,13 +358,11 @@ export const OAuthAppsList = () => {
confirmLabel="Confirm"
onCancel={() => setShowRegenerateDialog(false)}
onConfirm={() => {
if (selectedApp?.client_id) {
regenerateSecret({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
clientId: selectedApp.client_id,
})
}
regenerateSecret({
projectRef,
clientId: selectedApp?.client_id,
clientEndpoint: endpointData?.endpoint,
})
setShowRegenerateDialog(false)
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useOAuthServerAppsQuery } from 'data/oauth-server-apps/oauth-server-apps-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSupabaseClientQuery } from 'hooks/use-supabase-client-query'
import {
Button,
Card,
Expand Down Expand Up @@ -100,12 +99,7 @@ export const OAuthServerSettingsForm = () => {
isSuccess: isPermissionsLoaded,
} = useAsyncCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')

const { data: supabaseClientData } = useSupabaseClientQuery({ projectRef })

const { data: oAuthAppsData } = useOAuthServerAppsQuery({
projectRef,
supabaseClient: supabaseClientData?.supabaseClient,
})
const { data: oAuthAppsData } = useOAuthServerAppsQuery({ projectRef })

const oauthApps = oAuthAppsData?.clients || []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reac
import 'reactflow/dist/style.css'

import { BASE_PATH } from 'lib/constants'
import { Badge, Button, Card, CardContent } from 'ui'
import { Button, Card, CardContent } from 'ui'
import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'

const STATIC_NODES = [
Expand Down Expand Up @@ -157,7 +157,7 @@ const ReplicaNode = ({
<div className="flex items-start justify-between p-3" style={{ width: NODE_WIDTH / 2 - 10 }}>
<div className="flex gap-x-3">
<div className="flex flex-col gap-y-0.5">
<p className="">{data.label}</p>
<p>{data.label}</p>
<p className="flex items-center gap-x-1">
<span className="text-sm text-foreground-light">{data.details}</span>
</p>
Expand Down Expand Up @@ -196,20 +196,22 @@ const BlankNode = () => {

const CTANode = ({ projectRef }: { projectRef: string }) => {
return (
<Card className="w-[500px] p-6">
<Card className="w-[570px] p-6">
<CardContent>
<div className="flex items-center gap-x-2 justify-between mb-2">
<h2 className="text-lg">Replicate data to destinations in real-time</h2>
<Badge variant="warning">Early Access</Badge>
</div>
<h2 className="text-lg mb-2">Stream database changes to external destinations</h2>
<p className="text-foreground-light mb-2">
Automatically replicate your data to external data warehouses and analytics platforms in
real-time. No manual exports, no lag.
</p>
<p className="text-foreground-light">
Replicate database changes to multiple destinations - no manual exports, no lag. Limited
rollout for external destinations has begun, read replicas available now.
We are currently in <span className="text-foreground">private alpha</span> and slowly
onboarding new customers to ensure stable data pipelines. Request access below to join the
waitlist. Read replicas are available now.
</p>
<div className="flex items-center gap-x-2 mt-6">
<Button asChild type="secondary" iconRight={<ArrowUpRight size={16} strokeWidth={1.5} />}>
<Link href="https://forms.supabase.com/pg_replicate" target="_blank" rel="noreferrer">
Request early access
Request alpha access
</Link>
</Button>
<Button asChild type="default" iconRight={<ArrowRight size={16} strokeWidth={1.5} />}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useState } from 'react'
import { useParams } from 'common'
import { ScaffoldHeader, ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { AlphaNotice } from 'components/ui/AlphaNotice'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useAnalyticsBucketsQuery } from 'data/storage/analytics-buckets-query'
import { AnalyticsBucket as AnalyticsBucketIcon } from 'icons'
Expand All @@ -23,7 +24,6 @@ import {
} from 'ui'
import { TimestampInfo } from 'ui-patterns'
import { Input } from 'ui-patterns/DataInputs/Input'
import { AlphaNotice } from '../AlphaNotice'
import { EmptyBucketState } from '../EmptyBucketState'
import { CreateAnalyticsBucketModal } from './CreateAnalyticsBucketModal'

Expand Down Expand Up @@ -64,7 +64,10 @@ export const AnalyticsBuckets = () => {

return (
<ScaffoldSection isFullWidth>
<AlphaNotice type="analytics" />
<AlphaNotice
entity="Analytics buckets"
feedbackUrl="https://github.com/orgs/supabase/discussions/40116"
/>

{isLoadingBuckets && <GenericSkeletonLoader />}

Expand Down
Loading
Loading