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
3 changes: 2 additions & 1 deletion apps/docs/content/guides/auth/social-login/auth-google.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -801,4 +801,5 @@ chrome.identity.launchWebAuthFlow(

By default, the Google consent screen shows the root domain of the callback URL, where Google will send the authentication response. With Supabase Auth, it is your Supabase project's domain `(https://<your-project-ref>.supabase.co)`.

If that is not preferable, you can use a [Custom Domain](https://supabase.com/docs/guides/platform/custom-domains) with your Supabase project. You can use it as your project's domain when creating the Supabase client in your application and initiating the authentication flow. It will then show up in the Google consent screen. If you want your app name and the logo on the consent screen, [you must submit your app to Google for verification](https://support.google.com/cloud/answer/10311615).
You can change this domain in the settings for your Google app. Go to Google App Platform, click on Branding, and then set your application home page, privacy policy, and terms of use. You can also add your logo.
Next, go to Audience and click publish. Google will need to verify the app, which usually takes around 24 hours.
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,15 @@ const GitHubIntegrationConnectionForm = ({
enabled: Boolean(gitHubAuthorization),
})

const { mutate: updateBranch } = useBranchUpdateMutation()
const { mutate: updateBranch } = useBranchUpdateMutation({
onSuccess: () => {
toast.success('Production branch settings successfully updated')
},
})
const { mutate: createBranch } = useBranchCreateMutation({
onSuccess: () => {
toast.success('Production branch settings successfully updated')
},
onError: (error) => {
console.error('Failed to enable branching:', error)
},
Expand All @@ -105,7 +112,11 @@ const GitHubIntegrationConnectionForm = ({
useCheckGithubBranchValidity({ onError: () => {} })

const { mutate: createConnection, isLoading: isCreatingConnection } =
useGitHubConnectionCreateMutation()
useGitHubConnectionCreateMutation({
onSuccess: () => {
toast.success('GitHub integration successfully updated')
},
})

const { mutateAsync: deleteConnection, isLoading: isDeletingConnection } =
useGitHubConnectionDeleteMutation({
Expand Down Expand Up @@ -245,7 +256,6 @@ const GitHubIntegrationConnectionForm = ({
gitBranch: data.branchName,
})
}
toast.success('GitHub integration successfully updated')
}

const handleUpdateConnection = async (
Expand Down Expand Up @@ -289,7 +299,6 @@ const GitHubIntegrationConnectionForm = ({
})
}

toast.success('GitHub integration successfully updated')
setIsConfirmingBranchChange(false)
}

Expand Down Expand Up @@ -344,15 +353,6 @@ const GitHubIntegrationConnectionForm = ({
}
}

useEffect(() => {
if (selectedRepository) {
githubSettingsForm.setValue(
'branchName',
githubRepos.find((repo) => repo.id === selectedRepository.id)?.default_branch || 'main'
)
}
}, [selectedRepository])

useEffect(() => {
if (connection) {
const hasGitBranch = Boolean(prodBranch?.git_branch?.trim())
Expand Down Expand Up @@ -463,6 +463,10 @@ const GitHubIntegrationConnectionForm = ({
onSelect={() => {
field.onChange(repo.id)
setRepoComboboxOpen(false)
githubSettingsForm.setValue(
'branchName',
repo.default_branch || 'main'
)
}}
>
<div className="bg-black shadow rounded p-1 w-5 h-5 flex justify-center items-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Button, Form_Shadcn_, FormControl_Shadcn_, FormField_Shadcn_, Input_Sha
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'

const forgotPasswordSchema = z.object({
email: z.string().email('Must be a valid email').min(1, 'Email is required'),
email: z.string().min(1, 'Please provide an email address').email('Must be a valid email'),
})

const codeSchema = z.object({
Expand Down
171 changes: 91 additions & 80 deletions apps/studio/components/interfaces/SignIn/SignInForm.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { zodResolver } from '@hookform/resolvers/zod'
import * as Sentry from '@sentry/nextjs'
import type { AuthError } from '@supabase/supabase-js'
import { useQueryClient } from '@tanstack/react-query'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useRef, useState, useEffect } from 'react'
import { useEffect, useRef, useState } from 'react'
import { type SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { object, string } from 'yup'
import z from 'zod'

import { useAddLoginEvent } from 'data/misc/audit-login-mutation'
import { getMfaAuthenticatorAssuranceLevel } from 'data/profile/mfa-authenticator-assurance-level-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useLastSignIn } from 'hooks/misc/useLastSignIn'
import { auth, buildPathWithParams, getReturnToPath } from 'lib/gotrue'
import { Button, Form, Input } from 'ui'
import { Button, Form_Shadcn_, FormControl_Shadcn_, FormField_Shadcn_, Input_Shadcn_ } from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { LastSignInWrapper } from './LastSignInWrapper'

const signInSchema = object({
email: string().email('Must be a valid email').required('Email is required'),
password: string().required('Password is required'),
const schema = z.object({
email: z.string().min(1, 'Email is required').email('Must be a valid email'),
password: z.string().min(1, 'Password is required'),
})

const SignInForm = () => {
const formId = 'sign-in-form'

export const SignInForm = () => {
const router = useRouter()
const queryClient = useQueryClient()
const [_, setLastSignIn] = useLastSignIn()

const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const captchaRef = useRef<HCaptcha>(null)
const [returnTo, setReturnTo] = useState<string | null>(null)
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: { email: '', password: '' },
})
const isSubmitting = form.formState.isSubmitting

useEffect(() => {
// Only call getReturnToPath after component mounts client-side
Expand All @@ -44,7 +54,7 @@ const SignInForm = () => {
forgotPasswordUrl = `${forgotPasswordUrl}?returnTo=${encodeURIComponent(returnTo)}`
}

const onSignIn = async ({ email, password }: { email: string; password: string }) => {
const onSubmit: SubmitHandler<z.infer<typeof schema>> = async ({ email, password }) => {
const toastId = toast.loading('Signing in...')

let token = captchaToken
Expand Down Expand Up @@ -106,77 +116,78 @@ const SignInForm = () => {
}

return (
<Form
validateOnBlur
id="signIn-form"
initialValues={{ email: '', password: '' }}
validationSchema={signInSchema}
onSubmit={onSignIn}
>
{({ isSubmitting }: { isSubmitting: boolean }) => {
return (
<div className="flex flex-col gap-4">
<Input
id="email"
name="email"
type="email"
label="Email"
placeholder="[email protected]"
disabled={isSubmitting}
autoComplete="email"
/>

<div className="relative">
<Input
id="password"
name="password"
type="password"
label="Password"
placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;"
disabled={isSubmitting}
autoComplete="current-password"
/>

{/* positioned using absolute instead of labelOptional prop so tabbing between inputs works smoothly */}
<Link
href={forgotPasswordUrl}
className="absolute top-0 right-0 text-sm text-foreground-lighter"
>
Forgot Password?
</Link>
</div>

<div className="self-center">
<HCaptcha
ref={captchaRef}
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
size="invisible"
onVerify={(token) => {
setCaptchaToken(token)
}}
onExpire={() => {
setCaptchaToken(null)
}}
/>
</div>

<LastSignInWrapper type="email">
<Button
block
form="signIn-form"
htmlType="submit"
size="large"
disabled={isSubmitting}
loading={isSubmitting}
>
Sign In
</Button>
</LastSignInWrapper>
</div>
)
}}
</Form>
<Form_Shadcn_ {...form}>
<form id={formId} className="flex flex-col gap-4" onSubmit={form.handleSubmit(onSubmit)}>
<FormField_Shadcn_
key="email"
name="email"
control={form.control}
render={({ field }) => (
<FormItemLayout name="email" label="Email">
<FormControl_Shadcn_>
<Input_Shadcn_
id="email"
type="email"
autoComplete="email"
{...field}
placeholder="[email protected]"
disabled={isSubmitting}
/>
</FormControl_Shadcn_>
</FormItemLayout>
)}
/>

<div className="relative">
<FormField_Shadcn_
key="password"
name="password"
control={form.control}
render={({ field }) => (
<FormItemLayout name="password" label="Password">
<FormControl_Shadcn_>
<Input_Shadcn_
id="password"
type="password"
autoComplete="current-password"
{...field}
placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;"
disabled={isSubmitting}
/>
</FormControl_Shadcn_>
</FormItemLayout>
)}
/>

{/* positioned using absolute instead of labelOptional prop so tabbing between inputs works smoothly */}
<Link
href={forgotPasswordUrl}
className="absolute top-0 right-0 text-sm text-foreground-lighter"
>
Forgot Password?
</Link>
</div>

<div className="self-center">
<HCaptcha
ref={captchaRef}
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
size="invisible"
onVerify={(token) => {
setCaptchaToken(token)
}}
onExpire={() => {
setCaptchaToken(null)
}}
/>
</div>

<LastSignInWrapper type="email">
<Button block form={formId} htmlType="submit" size="large" loading={isSubmitting}>
Sign In
</Button>
</LastSignInWrapper>
</form>
</Form_Shadcn_>
)
}

export default SignInForm
Loading
Loading