♻️ (auth) Make sure new users have an email
Also fix after-auth redirections Closes #323
This commit is contained in:
24
apps/builder/src/features/auth/components/SignInError.tsx
Normal file
24
apps/builder/src/features/auth/components/SignInError.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Alert } from '@chakra-ui/react'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
error: string
|
||||||
|
}
|
||||||
|
const errors: Record<string, string> = {
|
||||||
|
Signin: 'Try signing with a different account.',
|
||||||
|
OAuthSignin: 'Try signing with a different account.',
|
||||||
|
OAuthCallback: 'Try signing with a different account.',
|
||||||
|
OAuthCreateAccount: 'Email not found. Try signing with a different provider.',
|
||||||
|
EmailCreateAccount: 'Try signing with a different account.',
|
||||||
|
Callback: 'Try signing with a different account.',
|
||||||
|
OAuthAccountNotLinked:
|
||||||
|
'To confirm your identity, sign in with the same account you used originally.',
|
||||||
|
CredentialsSignin:
|
||||||
|
'Sign in failed. Check the details you provided are correct.',
|
||||||
|
default: 'An error occurred. Please try again.',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SignInError = ({ error }: Props) => (
|
||||||
|
<Alert status="error" variant="solid" rounded="md">
|
||||||
|
{errors[error] ?? errors[error]}
|
||||||
|
</Alert>
|
||||||
|
)
|
@ -23,6 +23,7 @@ import { useRouter } from 'next/router'
|
|||||||
import { BuiltInProviderType } from 'next-auth/providers'
|
import { BuiltInProviderType } from 'next-auth/providers'
|
||||||
import { useToast } from '@/hooks/useToast'
|
import { useToast } from '@/hooks/useToast'
|
||||||
import { TextLink } from '@/components/TextLink'
|
import { TextLink } from '@/components/TextLink'
|
||||||
|
import { SignInError } from './SignInError'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
defaultEmail?: string
|
defaultEmail?: string
|
||||||
@ -48,8 +49,10 @@ export const SignInForm = ({
|
|||||||
!isLoadingProviders && Object.keys(providers ?? {}).length === 0
|
!isLoadingProviders && Object.keys(providers ?? {}).length === 0
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === 'authenticated')
|
if (status === 'authenticated') {
|
||||||
router.replace({ pathname: '/typebots', query: router.query })
|
router.replace(router.query.callbackUrl?.toString() ?? '/typebots')
|
||||||
|
return
|
||||||
|
}
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const providers = await getProviders()
|
const providers = await getProviders()
|
||||||
setProviders(providers ?? undefined)
|
setProviders(providers ?? undefined)
|
||||||
@ -131,6 +134,9 @@ export const SignInForm = ({
|
|||||||
</HStack>
|
</HStack>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{router.query.error && (
|
||||||
|
<SignInError error={router.query.error.toString()} />
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { stringify } from 'qs'
|
|||||||
import { BuiltInProviderType } from 'next-auth/providers'
|
import { BuiltInProviderType } from 'next-auth/providers'
|
||||||
import { GoogleLogo } from '@/components/GoogleLogo'
|
import { GoogleLogo } from '@/components/GoogleLogo'
|
||||||
import { AzureAdLogo, FacebookLogo, GitlabLogo } from './logos'
|
import { AzureAdLogo, FacebookLogo, GitlabLogo } from './logos'
|
||||||
|
import { omit } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
providers:
|
providers:
|
||||||
@ -28,7 +29,9 @@ export const SocialLoginButtons = ({ providers }: Props) => {
|
|||||||
const handleSignIn = async (provider: string) => {
|
const handleSignIn = async (provider: string) => {
|
||||||
setAuthLoading(provider)
|
setAuthLoading(provider)
|
||||||
await signIn(provider, {
|
await signIn(provider, {
|
||||||
callbackUrl: `/typebots?${stringify(query)}`,
|
callbackUrl:
|
||||||
|
query.callbackUrl?.toString() ??
|
||||||
|
`/typebots?${stringify(omit(query, 'error', 'callbackUrl'))}`,
|
||||||
})
|
})
|
||||||
setTimeout(() => setAuthLoading(undefined), 3000)
|
setTimeout(() => setAuthLoading(undefined), 3000)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ import {
|
|||||||
import { useAutoSave } from '@/hooks/useAutoSave'
|
import { useAutoSave } from '@/hooks/useAutoSave'
|
||||||
import { createWebhookQuery } from '@/features/blocks/integrations/webhook/queries/createWebhookQuery'
|
import { createWebhookQuery } from '@/features/blocks/integrations/webhook/queries/createWebhookQuery'
|
||||||
import { duplicateWebhookQuery } from '@/features/blocks/integrations/webhook/queries/duplicateWebhookQuery'
|
import { duplicateWebhookQuery } from '@/features/blocks/integrations/webhook/queries/duplicateWebhookQuery'
|
||||||
|
import { useSession } from 'next-auth/react'
|
||||||
|
|
||||||
const autoSaveTimeout = 10000
|
const autoSaveTimeout = 10000
|
||||||
|
|
||||||
@ -103,7 +104,8 @@ export const TypebotProvider = ({
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
typebotId: string
|
typebotId: string
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter()
|
const { status } = useSession()
|
||||||
|
const { push } = useRouter()
|
||||||
const { showToast } = useToast()
|
const { showToast } = useToast()
|
||||||
|
|
||||||
const { typebot, publishedTypebot, webhooks, isReadOnly, isLoading, mutate } =
|
const { typebot, publishedTypebot, webhooks, isReadOnly, isLoading, mutate } =
|
||||||
@ -246,15 +248,15 @@ export const TypebotProvider = ({
|
|||||||
}, [localTypebot])
|
}, [localTypebot])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoading) return
|
if (status !== 'authenticated' || isLoading) return
|
||||||
if (!typebot) {
|
if (!typebot) {
|
||||||
showToast({ status: 'info', description: "Couldn't find typebot" })
|
showToast({ status: 'info', description: "Couldn't find typebot" })
|
||||||
router.replace('/typebots')
|
push('/typebots')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLocalTypebot({ ...typebot })
|
setLocalTypebot({ ...typebot })
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isLoading])
|
}, [status, isLoading])
|
||||||
|
|
||||||
const updateLocalTypebot = (updates: UpdateTypebotPayload) =>
|
const updateLocalTypebot = (updates: UpdateTypebotPayload) =>
|
||||||
localTypebot && setLocalTypebot({ ...localTypebot, ...updates })
|
localTypebot && setLocalTypebot({ ...localTypebot, ...updates })
|
||||||
|
@ -25,7 +25,7 @@ const { ToastContainer, toast } = createStandaloneToast(customTheme)
|
|||||||
const App = ({
|
const App = ({
|
||||||
Component,
|
Component,
|
||||||
pageProps: { session, ...pageProps },
|
pageProps: { session, ...pageProps },
|
||||||
}: AppProps<{ session: Session }>) => {
|
}: AppProps<{ session?: Session }>) => {
|
||||||
useRouterProgressBar()
|
useRouterProgressBar()
|
||||||
const { query, pathname, isReady } = useRouter()
|
const { query, pathname, isReady } = useRouter()
|
||||||
|
|
||||||
|
@ -146,6 +146,9 @@ const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
session: {
|
session: {
|
||||||
strategy: 'database',
|
strategy: 'database',
|
||||||
},
|
},
|
||||||
|
pages: {
|
||||||
|
signIn: '/signin',
|
||||||
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
session: async ({ session, user }) => {
|
session: async ({ session, user }) => {
|
||||||
const userFromDb = user as User
|
const userFromDb = user as User
|
||||||
|
@ -14,6 +14,8 @@ import {
|
|||||||
export function CustomAdapter(p: PrismaClient): Adapter {
|
export function CustomAdapter(p: PrismaClient): Adapter {
|
||||||
return {
|
return {
|
||||||
createUser: async (data: Omit<AdapterUser, 'id'>) => {
|
createUser: async (data: Omit<AdapterUser, 'id'>) => {
|
||||||
|
if (!data.email)
|
||||||
|
throw Error('Provider did not forward email but it is required')
|
||||||
const user = { id: createId(), email: data.email as string }
|
const user = { id: createId(), email: data.email as string }
|
||||||
const { invitations, workspaceInvitations } = await getNewUserInvitations(
|
const { invitations, workspaceInvitations } = await getNewUserInvitations(
|
||||||
p,
|
p,
|
||||||
|
Reference in New Issue
Block a user