From 2607721895bc126ed635ee841cd5ff71a9cf61c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Thu, 8 Aug 2024 17:59:54 +0200 Subject: [PATCH] Add alternative Vercel Marketplace flow with for popup windows --- apps/studio/.env | 3 +- .../pages/sign-in-vercel-marketplace-v2.tsx | 217 ++++++++++++++++++ 2 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 apps/studio/pages/sign-in-vercel-marketplace-v2.tsx diff --git a/apps/studio/.env b/apps/studio/.env index 4de7d26d76e..5c8eb9662c2 100644 --- a/apps/studio/.env +++ b/apps/studio/.env @@ -6,7 +6,6 @@ DEFAULT_PROJECT_NAME=Default Project DASHBOARD_USERNAME=supabase DASHBOARD_PASSWORD=this_password_is_insecure_and_should_be_updated - SUPABASE_URL=http://localhost:8000 SUPABASE_PUBLIC_URL=http://localhost:8000 SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE @@ -24,4 +23,4 @@ NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001 NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzUwOTQ4MzUsImV4cCI6MTk5MDY3MDgzNX0.0PMlOxtKL4O9GGZuAP_Xl4f-Tut1qOnW4bNEmAtoB8w NEXT_PUBLIC_SUPABASE_URL=https://xguihxuzqibwxjnimxev.supabase.co -DOCKER_SOCKET_LOCATION=/var/run/docker.sock \ No newline at end of file +DOCKER_SOCKET_LOCATION=/var/run/docker.sock diff --git a/apps/studio/pages/sign-in-vercel-marketplace-v2.tsx b/apps/studio/pages/sign-in-vercel-marketplace-v2.tsx new file mode 100644 index 00000000000..a1747cde6d5 --- /dev/null +++ b/apps/studio/pages/sign-in-vercel-marketplace-v2.tsx @@ -0,0 +1,217 @@ +import { useEffect, useState } from 'react' +import toast from 'react-hot-toast' +import * as Sentry from '@sentry/nextjs' + +import { useRouter } from 'next/router' +import { useIsLoggedIn, useUser } from 'common' +import Image from 'next/image' +import Link from 'next/link' +import { useTheme } from 'next-themes' +import { Button } from 'ui' +import { get } from 'data/fetchers' +import { getDisplayName } from 'components/interfaces/Auth/Users/UserListItem.utils' + +type VercelAuthState = 'invalid_code' + +const SignInVercelMarketplace = () => { + const { resolvedTheme } = useTheme() + const [loading, setLoading] = useState(false) + const [status, setStatus] = useState(null) + const isLoggedIn = useIsLoggedIn() + const router = useRouter() + const user = useUser() + + useEffect(() => { + if (!isLoggedIn) { + return + } + + async function verifyVercelIdentity() { + setLoading(true) + // @ts-ignore Kamil: no types regenerated for this, as this is just hacking around different authentication flows + const response = await get(`/partners/vercel/identity/verify`, { + params: { + query: { + code: router.query.code, + }, + }, + }).finally(async () => { + // TODO: Remove this. For demo purposes only. + await new Promise((resolve) => setTimeout(resolve, 1000)) + setLoading(false) + }) + + // if (response.response.status === 200) { + // if (router.query.resource_id) { + // router.push(`/project/${router.query.resource_id}`) + // } else { + // router.push(`/projects`) + // } + // return + // } + + if (response.response.status === 400) { + setStatus('invalid_code') + } + } + + verifyVercelIdentity() + }, [isLoggedIn, router]) + + // Flow without invites/approvals for now. + // + // Is user signed in? + // NO: + // - [x] if code is missing in query params, ask users to go through Vercel Dashboard; + // - [x] write an explanation that they need to login or sign-up; + // - [x] create 2 buttons that redirect to dashboard and preserve query string (it might be difficult for sign-up as it requires email confirmation); + // - [x] after login, redirect back to this page and perform [YES] flow; + // - [x] if code is missing in query params, ask users to go through Vercel Dashboard + // YES: + // - send API request to validate that user has provided identity assigned to this account + // - if yes, we are done - redirect to dashboard using provided `resource_id` if present or projects list if not + // - if not, write explainer that someone is signed with an account that is not linked with this Vercel user + // - they need to either login to a different account or they can link this account as well(???) + // - if there is no identity linked yet, show prompt to confirm account linking and perform API call to link accounts if they do so + // - on successful linking, redirect to the dashboard using provided `resource_id` if present or projects list if not + + // TODO(kamil): Sign-up link do not preserve query strings which we need to redirect after successful sign-up. + // This might not be needed if we won't get pop-up flow available anyway. + async function onSignInWithVercelMarketplace() { + if (isLoggedIn) { + // @ts-ignore - this is + const { response } = await get(`/partners/vercel/identity/verify`, { + params: { + query: { + ...router.query, + }, + }, + }) + + console.log({ response }) + } else { + try { + router.push({ + pathname: 'sign-in', + query: { + ...router.query, + returnTo: 'sign-in-vercel-marketplace', + }, + }) + // router.push(vercel_resource_id ? `/project/${vercel_resource_id}` : `/projects`) + } catch (error: any) { + toast.error(`Failed to sign in via Vercel Marketplace: ${error.message}`) + Sentry.captureMessage( + '[CRITICAL] Failed to sign in via Vercel Marketplace: ' + error.message + ) + setLoading(false) + } + } + } + + return ( +
+
+ +
+ {!router.query.code && ( +

+ A SSO exchange code is required to login. +
+ Go back to Vercel Marketplace and try again. +

+ )} + {router.query.code && + (isLoggedIn ? ( + <> +
+

Logged in as {getDisplayName(user!, user!.email)}

+ {loading ? ( +

Verifying linked account...

+ ) : ( +
+ {status === 'invalid_code' && ( +

+ Invalid SSO exchange code. +
+ Go back to Vercel Marketplace and try again. +

+ )} + +
+ )} + {/* CASE 1: This account is not linked anywhere, show "link account" button and redirect afterwards */} + {/* CASE 2: This account is correctly linked and we can automatically redirect (show button for now) */} + {/* CASE 3: This account is already linked to other account, show logout button and tell to login with different account */} +
+ + ) : ( +
+

Sign In to Continue

+

+ You need to be logged in on order to continue. +
+ Don't have an account?{' '} + + Sign Up Now + {' '} + and login +
+ via Vercel Marketplace to link your accounts. +

+
+ +
+
+ ))} +
+ ) +} + +export default SignInVercelMarketplace