fixes vercel and database.new integrations

by having both ?next=  (external) and ?redirectTo= (internal)
This commit is contained in:
Alaister Young authored and Joshen Lim committed 2022-11-22 16:41:05 +08:00
1 parent ec9cd56e27
commit e7b0f5b823
6 files changed
+105 -26

No files matched your search

@@ -1,8 +1,8 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { useStore } from 'hooks'
import { auth, getNextPath } from 'lib/gotrue'
import { usePushNext } from 'hooks/misc/useAutoAuthRedirect'
import { auth } from 'lib/gotrue'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useRef, useState } from 'react'
import { useSWRConfig } from 'swr'
import { Button, Form, Input } from 'ui'
@@ -15,7 +15,7 @@ const signInSchema = object({
const SignInForm = () => {
const { ui } = useStore()
const router = useRouter()
const pushNext = usePushNext()
const { cache } = useSWRConfig()
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
@@ -50,7 +50,7 @@ const SignInForm = () => {
// @ts-ignore
cache.clear()
await router.push(getNextPath())
await pushNext()
} else {
setCaptchaToken(null)
captchaRef.current?.resetCaptcha()
@@ -1,4 +1,4 @@
import { auth, getNextPath } from 'lib/gotrue'
import { auth, getReturnToPath } from 'lib/gotrue'
import { Button, IconGitHub } from 'ui'
const SignInWithGitHub = () => {
@@ -11,7 +11,7 @@ const SignInWithGitHub = () => {
process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
? process.env.NEXT_PUBLIC_VERCEL_URL
: process.env.NEXT_PUBLIC_SITE_URL
}${getNextPath()}`,
}${getReturnToPath()}`,
},
})
if (error) throw error
@@ -1,11 +1,13 @@
import { useStore } from 'hooks'
import { getNextPath, STORAGE_KEY } from 'lib/gotrue'
import { usePushNext } from 'hooks/misc/useAutoAuthRedirect'
import { auth, getReturnToPath, STORAGE_KEY } from 'lib/gotrue'
import { observer } from 'mobx-react-lite'
import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
import { PropsWithChildren, useEffect, useState } from 'react'
import { tweets } from 'shared-data'
import { useSWRConfig } from 'swr'
import { Button, IconFileText } from 'ui'
type SignInLayoutProps = {
@@ -22,9 +24,44 @@ const SignInLayout = ({
logoLinkToMarketingSite = false,
children,
}: PropsWithChildren<SignInLayoutProps>) => {
const pushNext = usePushNext()
const { ui } = useStore()
const { cache } = useSWRConfig()
const { theme } = ui
useEffect(() => {
const searchParams = new URLSearchParams(location.search)
const hasReturnTo = searchParams.has('returnTo')
const hasNext = searchParams.has('next')
const shouldRedirect = !hasReturnTo && !hasNext
if (!shouldRedirect) {
// If there's a returnTo or next, then this redirect will be handled by useAutoAuthRedirect() in _app.tsx
return
}
;(async () => {
const { error } = await auth.initialize()
if (error) {
// if there was a problem signing in via the url, don't redirect
return
}
const {
data: { session },
} = await auth.getSession()
if (session) {
// .clear() does actually exist on the cache object, but it's not in the types 🤦🏻
// @ts-ignore
cache.clear()
await pushNext()
}
})()
}, [])
const [quote, setQuote] = useState<{
text: string
url: string
@@ -43,7 +80,7 @@ const SignInLayout = ({
<Head>
<script
dangerouslySetInnerHTML={{
__html: `window._getNextPath = ${getNextPath.toString()};if (localStorage.getItem('${STORAGE_KEY}')) {location.replace(window._getNextPath())}`,
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (localStorage.getItem('${STORAGE_KEY}') && !(new URLSearchParams(location.search).has('next'))) {location.replace(window._getReturnToPath())}`,
}}
/>
</Head>
+54 -7
View File
@@ -1,17 +1,64 @@
import { auth, getNextPath } from 'lib/gotrue'
import { auth, getReturnToPath } from 'lib/gotrue'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { useCallback, useEffect } from 'react'
import { useSWRConfig } from 'swr'
function useAutoAuthRedirect() {
export function usePushNext() {
const router = useRouter()
return useCallback(
async () => {
const searchParams = new URLSearchParams(location.search)
const hasNext = searchParams.has('next')
// preference the next param, as it's used for integration redirects
if (hasNext) {
const next = searchParams.get('next')!
// vercel integration
const isVercelIntegration = next.includes('https://vercel.com')
if (isVercelIntegration) {
searchParams.delete('returnTo')
return await router.push(`/vercel/integrate?${searchParams.toString()}`)
}
// database.new integration
if (next === 'new-project') {
searchParams.delete('next')
searchParams.delete('returnTo')
const remainingSearchParams = searchParams.toString()
return await router.push(
'/new/new-project' + (remainingSearchParams ? `?${remainingSearchParams}` : '')
)
}
return await router.push(
next + (searchParams.toString() ? `?${searchParams.toString()}` : '')
)
}
return await router.push(getReturnToPath())
},
// watch params so router.query will get updated
[router.query]
)
}
function useAutoAuthRedirect() {
const pushNext = usePushNext()
const { cache } = useSWRConfig()
useEffect(() => {
const returnTo = new URLSearchParams(location.search).get('next')
const searchParams = new URLSearchParams(location.search)
const hasReturnTo = searchParams.has('returnTo')
const hasNext = searchParams.has('next')
const shouldRedirect = hasReturnTo || hasNext
if (!returnTo) {
// If there's no returnTo, then we don't need to do anything
if (!shouldRedirect) {
// If there's no returnTo or next, then we don't need to do anything
return
}
@@ -32,7 +79,7 @@ function useAutoAuthRedirect() {
// @ts-ignore
cache.clear()
await router.push(getNextPath())
await pushNext()
}
})()
}, [])
+2 -2
View File
@@ -2,7 +2,7 @@ import { ComponentType, useEffect } from 'react'
import Head from 'next/head'
import { NextRouter, useRouter } from 'next/router'
import { getNextPath, STORAGE_KEY } from 'lib/gotrue'
import { getReturnToPath, STORAGE_KEY } from 'lib/gotrue'
import { IS_PLATFORM } from 'lib/constants'
import { useProfile, useStore, usePermissions } from 'hooks'
import Error500 from '../../pages/500'
@@ -86,7 +86,7 @@ export function withAuth<T>(
to the login page if they are guaranteed (no token at all) to not be logged in. */}
<script
dangerouslySetInnerHTML={{
__html: `window._getNextPath = ${getNextPath.toString()};if (!localStorage.getItem('${STORAGE_KEY}') && !location.hash) {location.replace('/sign-in?next=' + window._getNextPath(location.pathname))}`,
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (!localStorage.getItem('${STORAGE_KEY}') && !location.hash) {const searchParams = new URLSearchParams(location.search);searchParams.set('returnTo', location.pathname);location.replace('/sign-in' + '?' + searchParams.toString())}`,
}}
/>
</Head>
+4 -9
View File
@@ -40,18 +40,13 @@ export const getIdentity = (gotrueUser: User) => {
// NOTE: do not use any imports in this function,
// as it is use standalone in the documents head
export const getNextPath = (fallback = '/projects') => {
export const getReturnToPath = (fallback = '/projects') => {
const searchParams = new URLSearchParams(location.search)
const returnTo = searchParams.get('next')
let returnTo = searchParams.get('returnTo') ?? fallback
searchParams.delete('next')
searchParams.delete('returnTo')
const next = returnTo ?? fallback
const remainingSearchParams = searchParams.toString()
if (next === 'new-project') {
return '/new/new-project' + (remainingSearchParams ? `?${remainingSearchParams}` : '')
}
return next + (remainingSearchParams ? `?${remainingSearchParams}` : '')
return returnTo + (remainingSearchParams ? `?${remainingSearchParams}` : '')
}