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
2 changes: 1 addition & 1 deletion apps/docs/content/_partials/quickstart_db_setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ curl -X POST https://api.supabase.com/v1/projects \
"organization_id": "<org-id>",
"name": "My Project",
"region": "us-east-1",
"password": "<your-secure-password>"
"db_pass": "<your-secure-password>"
}'
```

Expand Down
120 changes: 55 additions & 65 deletions apps/docs/content/guides/auth/social-login/auth-google.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -376,82 +376,72 @@ If you're integrating Google One-Tap with your Next.js application, you can refe

import Script from 'next/script'
import { createClient } from '@/utils/supabase/client'
import { CredentialResponse } from 'google-one-tap'
import type { accounts, CredentialResponse } from 'google-one-tap'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'

declare const google: { accounts: accounts }

// generate nonce to use for google id token sign-in
const generateNonce = async (): Promise<string[]> => {
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
const encoder = new TextEncoder()
const encodedNonce = encoder.encode(nonce)
const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')

return [nonce, hashedNonce]
}

const OneTapComponent = () => {
const supabase = createClient()
const router = useRouter()

// generate nonce to use for google id token sign-in
const generateNonce = async (): Promise<string[]> => {
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
const encoder = new TextEncoder()
const encodedNonce = encoder.encode(nonce)
const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
const initializeGoogleOneTap = () => {
console.log('Initializing Google One Tap')
const [nonce, hashedNonce] = await generateNonce()
console.log('Nonce: ', nonce, hashedNonce)

return [nonce, hashedNonce]
}
// check if there's already an existing session before initializing the one-tap UI
const { data, error } = await supabase.auth.getSession()
if (error) {
console.error('Error getting session', error)
}
if (data.session) {
router.push('/')
return
}

useEffect(() => {
const initializeGoogleOneTap = () => {
console.log('Initializing Google One Tap')
window.addEventListener('load', async () => {
const [nonce, hashedNonce] = await generateNonce()
console.log('Nonce: ', nonce, hashedNonce)

// check if there's already an existing session before initializing the one-tap UI
const { data, error } = await supabase.auth.getSession()
if (error) {
console.error('Error getting session', error)
}
if (data.session) {
/* global google */
google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: async (response: CredentialResponse) => {
try {
// send id token returned in response.credential to supabase
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.credential,
nonce,
})

if (error) throw error
console.log('Session data: ', data)
console.log('Successfully logged in with Google One Tap')

// redirect to protected page
router.push('/')
return
} catch (error) {
console.error('Error logging in with Google One Tap', error)
}
},
nonce: hashedNonce,
// with chrome's removal of third-party cookies, we need to use FedCM instead (https://developers.google.com/identity/gsi/web/guides/fedcm-migration)
use_fedcm_for_prompt: true,
})
google.accounts.id.prompt() // Display the One Tap UI
}

/* global google */
google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: async (response: CredentialResponse) => {
try {
// send id token returned in response.credential to supabase
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.credential,
nonce,
})

if (error) throw error
console.log('Session data: ', data)
console.log('Successfully logged in with Google One Tap')

// redirect to protected page
router.push('/')
} catch (error) {
console.error('Error logging in with Google One Tap', error)
}
},
nonce: hashedNonce,
// with chrome's removal of third-party cookies, we need to use FedCM instead (https://developers.google.com/identity/gsi/web/guides/fedcm-migration)
use_fedcm_for_prompt: true,
})
google.accounts.id.prompt() // Display the One Tap UI
})
}
initializeGoogleOneTap()
return () => window.removeEventListener('load', initializeGoogleOneTap)
}, [])

return (
<>
<Script src="https://accounts.google.com/gsi/client" />
<div id="oneTap" className="fixed top-0 right-0 z-[100]" />
</>
)
return <Script onReady={initializeGoogleOneTap} src="https://accounts.google.com/gsi/client" />
}

export default OneTapComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from 'react'

import { FeatureFlagContext, LOCAL_STORAGE_KEYS } from 'common'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useFlag, useIsRealtimeSettingsFFEnabled } from 'hooks/ui/useFlag'
import { EMPTY_OBJ } from 'lib/void'
import { FEATURE_PREVIEWS } from './FeaturePreview.constants'
Expand Down Expand Up @@ -91,8 +92,12 @@ export const useIsInlineEditorEnabled = () => {
}

export const useUnifiedLogsPreview = () => {
const organization = useSelectedOrganization()
const { flags, onUpdateFlag } = useFeaturePreviewContext()
const isEnabled = flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]

const isTeamsOrEnterprise = ['team', 'enterprise'].includes(organization?.plan.id ?? '')
const isEnabled = isTeamsOrEnterprise && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]

const enable = () => onUpdateFlag(LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS, true)
const disable = () => onUpdateFlag(LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS, false)
return { isEnabled, enable, disable }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const UnifiedLogsPreview = () => {
Experience our enhanced logs interface with improved filtering, real-time updates, and a
unified view across all your services. Built for better performance and easier debugging.
</p>
<p className="text-foreground-light text-sm mb-4">
This interface will only be available for organizations on the Team plan or above.
</p>
<div className="space-y-2 !mt-4">
<p className="text-sm">Enabling this preview will:</p>
<ul className="list-disc pl-6 text-sm text-foreground-light space-y-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ const getBaseEdgeServiceFlowQuery = (logId: string, serviceType: EdgeServiceType
apikey_payload.issuer = 'supabase' AND
apikey_payload.role IN ('anon', 'service_role')
THEN apikey_payload.role
WHEN sb_apikey.invalid IS NOT NULL THEN '<invalid>'
WHEN apikey_payload IS NOT NULL THEN '<unrecognized>'
ELSE NULL
END as jwt_key_role,
Expand Down Expand Up @@ -120,21 +119,21 @@ const getBaseEdgeServiceFlowQuery = (logId: string, serviceType: EdgeServiceType
-- JWT data
apikey_payload.role as jwt_apikey_role,
apikey_payload.algorithm as jwt_apikey_algorithm,
null as jwt_apikey_expires_at,
apikey_payload.expires_at as jwt_apikey_expires_at,
apikey_payload.issuer as jwt_apikey_issuer,
null as jwt_apikey_signature_prefix,
apikey_payload.signature_prefix as jwt_apikey_signature_prefix,
null as jwt_apikey_key_id,
null as jwt_apikey_session_id,
null as jwt_apikey_subject,
apikey_payload.subject as jwt_apikey_subject,

authorization_payload.role as jwt_auth_role,
authorization_payload.algorithm as jwt_auth_algorithm,
null as jwt_auth_expires_at,
authorization_payload.expires_at as jwt_auth_expires_at,
authorization_payload.issuer as jwt_auth_issuer,
null as jwt_auth_signature_prefix,
null as jwt_auth_key_id,
null as jwt_auth_session_id,
null as jwt_auth_subject,
authorization_payload.signature_prefix as jwt_auth_signature_prefix,
authorization_payload.key_id as jwt_auth_key_id,
authorization_payload.session_id as jwt_auth_session_id,
authorization_payload.subject as jwt_auth_subject,

-- Storage specific data (included for all but only populated for storage)
edge_logs_response_headers.sb_gateway_mode as storage_edge_gateway_mode,
Expand All @@ -153,8 +152,8 @@ const getBaseEdgeServiceFlowQuery = (logId: string, serviceType: EdgeServiceType
left join unnest(edge_logs_request.cf) as edge_logs_cf
left join unnest(edge_logs_request.sb) as sb
left join unnest(sb.jwt) as jwt
left join unnest(COALESCE(jwt.apikey, [])) as sb_apikey
left join unnest(COALESCE(sb_apikey.payload, [])) as apikey_payload
left join unnest(COALESCE(jwt.apikey, [])) as apikey
left join unnest(COALESCE(apikey.payload, [])) as apikey_payload
left join unnest(COALESCE(jwt.authorization, [])) as auth
left join unnest(COALESCE(auth.payload, [])) as authorization_payload
left join unnest(COALESCE(sb.apikey, [])) as sb_apikey_outer
Expand Down Expand Up @@ -246,7 +245,6 @@ export const getEdgeFunctionServiceFlowQuery = (logId: string): string => {
apikey_payload.issuer = 'supabase' AND
apikey_payload.role IN ('anon', 'service_role')
THEN apikey_payload.role
WHEN sb_apikey.invalid IS NOT NULL THEN '<invalid>'
WHEN apikey_payload IS NOT NULL THEN '<unrecognized>'
ELSE NULL
END as jwt_key_role,
Expand All @@ -273,21 +271,21 @@ export const getEdgeFunctionServiceFlowQuery = (logId: string): string => {
-- JWT data
apikey_payload.role as jwt_apikey_role,
apikey_payload.algorithm as jwt_apikey_algorithm,
null as jwt_apikey_expires_at,
apikey_payload.expires_at as jwt_apikey_expires_at,
apikey_payload.issuer as jwt_apikey_issuer,
null as jwt_apikey_signature_prefix,
apikey_payload.signature_prefix as jwt_apikey_signature_prefix,
null as jwt_apikey_key_id,
null as jwt_apikey_session_id,
null as jwt_apikey_subject,
apikey_payload.subject as jwt_apikey_subject,

authorization_payload.role as jwt_auth_role,
authorization_payload.algorithm as jwt_auth_algorithm,
null as jwt_auth_expires_at,
authorization_payload.expires_at as jwt_auth_expires_at,
authorization_payload.issuer as jwt_auth_issuer,
null as jwt_auth_signature_prefix,
null as jwt_auth_key_id,
null as jwt_auth_session_id,
null as jwt_auth_subject,
authorization_payload.signature_prefix as jwt_auth_signature_prefix,
authorization_payload.key_id as jwt_auth_key_id,
authorization_payload.session_id as jwt_auth_session_id,
authorization_payload.subject as jwt_auth_subject,

-- Function logs aggregation
function_logs_agg.function_log_count as function_log_count,
Expand All @@ -305,8 +303,8 @@ export const getEdgeFunctionServiceFlowQuery = (logId: string): string => {
left join unnest(fel_request.headers) as fel_request_headers
left join unnest(fel_request.sb) as sb
left join unnest(sb.jwt) as jwt
left join unnest(COALESCE(jwt.apikey, [])) as sb_apikey
left join unnest(COALESCE(sb_apikey.payload, [])) as apikey_payload
left join unnest(COALESCE(jwt.apikey, [])) as apikey
left join unnest(COALESCE(apikey.payload, [])) as apikey_payload
left join unnest(COALESCE(jwt.authorization, [])) as auth
left join unnest(COALESCE(auth.payload, [])) as authorization_payload
left join unnest(COALESCE(sb.apikey, [])) as sb_apikey_outer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const filterFields = [
defaultOpen: false,
options: [],
hasDynamicOptions: true,
hasAsyncSearch: true,
hasAsyncSearch: false,
component: (props: Option) => {
return (
<span className="truncate block w-full text-[0.75rem]" title={props.value as string}>
Expand All @@ -114,25 +114,6 @@ export const filterFields = [
)
},
},
{
label: 'Auth User',
value: 'auth_user',
type: 'checkbox',
defaultOpen: false,
options: [],
hasDynamicOptions: true,
hasAsyncSearch: true,
component: (props: Option) => {
return (
<div className="flex items-center gap-2 min-w-0">
<User size={14} className="text-foreground-lighter flex-shrink-0" />
<span className="truncate text-[0.75rem]" title={props.value as string}>
{props.value}
</span>
</div>
)
},
},
] satisfies DataTableFilterField<ColumnSchema>[]

export const sheetFields = [
Expand Down
Loading
Loading