From cf2de848515bc679814ce82abc7857cc4914ccf9 Mon Sep 17 00:00:00 2001 From: Tim Routowicz Date: Sun, 25 Feb 2024 12:32:51 -0500 Subject: [PATCH 1/2] Use server side auth --- app/.env.local.template | 2 + app/next.config.mjs | 1 - app/package-lock.json | 30 +++++++++ app/package.json | 1 + app/src/components/LoginForm.tsx | 86 ++++++------------------- app/src/components/LogoutButton.tsx | 9 ++- app/src/pages/index.tsx | 26 ++++++-- app/src/pages/login.tsx | 24 +++++++ app/src/utils/supabase/component.ts | 10 +++ app/src/utils/supabase/server-props.ts | 31 +++++++++ app/src/utils/useSupabaseConfig.tsx | 89 -------------------------- 11 files changed, 147 insertions(+), 162 deletions(-) create mode 100644 app/.env.local.template create mode 100644 app/src/pages/login.tsx create mode 100644 app/src/utils/supabase/component.ts create mode 100644 app/src/utils/supabase/server-props.ts delete mode 100644 app/src/utils/useSupabaseConfig.tsx diff --git a/app/.env.local.template b/app/.env.local.template new file mode 100644 index 0000000..c4c451c --- /dev/null +++ b/app/.env.local.template @@ -0,0 +1,2 @@ +NEXT_PUBLIC_SUPABASE_URL= +NEXT_PUBLIC_SUPABASE_ANON_KEY= diff --git a/app/next.config.mjs b/app/next.config.mjs index b199633..6b54a6c 100644 --- a/app/next.config.mjs +++ b/app/next.config.mjs @@ -1,7 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: false, - output: 'export', images: { unoptimized: true, }, diff --git a/app/package-lock.json b/app/package-lock.json index 3ff4ceb..8cd31d2 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -18,6 +18,7 @@ "@radix-ui/react-slot": "^1.0.2", "@supabase/auth-helpers-nextjs": "^0.8.7", "@supabase/auth-helpers-react": "^0.4.2", + "@supabase/ssr": "^0.1.0", "@supabase/supabase-js": "^2.39.3", "@tanstack/react-query": "^5.18.1", "class-variance-authority": "^0.7.0", @@ -1494,6 +1495,18 @@ "ws": "^8.14.2" } }, + "node_modules/@supabase/ssr": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.1.0.tgz", + "integrity": "sha512-bIVrkqjAK5G3KjkIMKYKtAOlCgRRplEWjrlyRyXSOYtgDieiOhk2ZyNAPsEOa1By9OZVxuX5eAW1fitdnuxayw==", + "dependencies": { + "cookie": "^0.5.0", + "ramda": "^0.29.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.33.1" + } + }, "node_modules/@supabase/storage-js": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.5.tgz", @@ -2401,6 +2414,14 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5196,6 +5217,15 @@ } ] }, + "node_modules/ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", diff --git a/app/package.json b/app/package.json index ce6a579..f24f3bc 100644 --- a/app/package.json +++ b/app/package.json @@ -27,6 +27,7 @@ "@radix-ui/react-slot": "^1.0.2", "@supabase/auth-helpers-nextjs": "^0.8.7", "@supabase/auth-helpers-react": "^0.4.2", + "@supabase/ssr": "^0.1.0", "@supabase/supabase-js": "^2.39.3", "@tanstack/react-query": "^5.18.1", "class-variance-authority": "^0.7.0", diff --git a/app/src/components/LoginForm.tsx b/app/src/components/LoginForm.tsx index 6925967..d87546b 100644 --- a/app/src/components/LoginForm.tsx +++ b/app/src/components/LoginForm.tsx @@ -1,50 +1,32 @@ -import { useSupabaseConfig } from '@/utils/useSupabaseConfig'; -import { createClient } from '@supabase/supabase-js'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { toast } from 'sonner'; + import { Button } from './ui/button'; +import { createClient } from '@/utils/supabase/component'; export default function LoginForm() { const router = useRouter(); - - const [supabaseUrl, setSupabaseUrl] = useState(''); - const [supabaseToken, setSupabaseToken] = useState(''); + const supabase = createClient(); const [password, setPassword] = useState(''); const [email, setEmail] = useState(''); - const { - supabaseUrl: savedUrl, - supabaseToken: savedToken, - setSupabaseConfig, - } = useSupabaseConfig(); - - useEffect(() => { - if (savedUrl) { - setSupabaseUrl(savedUrl); - } - if (savedToken) { - setSupabaseToken(savedToken); - } - }, [savedUrl, savedToken]); + async function handleLogin(e: React.FormEvent) { + e.preventDefault(); - async function emailLogin() { try { - const supabaseClient = createClient(supabaseUrl, supabaseToken); - const { data, error } = await supabaseClient.auth.signInWithPassword({ + const { error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { toast.error(error.message); - } else { - toast.success('Login successful'); - setSupabaseConfig(supabaseUrl, supabaseToken); - router.reload(); + return; } + toast.success('Login successful'); + router.reload(); } catch (error: any) { - console.error('ERROR', error); toast.error(error.message || error.code || error.msg || 'Unknown error'); } } @@ -52,45 +34,15 @@ export default function LoginForm() { return (

Login to ADeus

- -
-
- - setSupabaseUrl(e.target.value)} - className="form-input h-10 w-full rounded-md border-2 pl-2" - required - /> -
- -
- - setSupabaseToken(e.target.value)} - className="form-input h-10 w-full rounded-md border-2 pl-2" - required - /> -
- +
setEmail(e.target.value)} @@ -112,16 +64,20 @@ export default function LoginForm() { required />
-
-
-
+

Don't have these details? Please check the setup guide{' '} - + here

diff --git a/app/src/components/LogoutButton.tsx b/app/src/components/LogoutButton.tsx index b88b1fa..cbd8e57 100644 --- a/app/src/components/LogoutButton.tsx +++ b/app/src/components/LogoutButton.tsx @@ -1,5 +1,7 @@ import { SupabaseClient } from '@supabase/supabase-js'; import { LogOut } from 'lucide-react'; +import { useRouter } from 'next/router'; + import { Button } from './ui/button'; export default function LogoutButton({ @@ -7,11 +9,16 @@ export default function LogoutButton({ }: { supabaseClient: SupabaseClient; }) { + const router = useRouter(); + return ( diff --git a/app/src/pages/index.tsx b/app/src/pages/index.tsx index d7df913..7d051f8 100644 --- a/app/src/pages/index.tsx +++ b/app/src/pages/index.tsx @@ -1,13 +1,27 @@ +import type { GetServerSidePropsContext } from 'next'; + +import { createClient as createServerClient } from '@/utils/supabase/server-props'; +import { createClient } from '@/utils/supabase/component'; import Chat from '@/components/Chat'; -import LoginForm from '@/components/LoginForm'; -import { useSupabase } from '@/utils/useSupabaseConfig'; export default function Index() { - const { user, supabaseClient } = useSupabase(); + const supabase = createClient(); + + return ; +} + +export async function getServerSideProps(context: GetServerSidePropsContext) { + const supabase = createServerClient(context); + const { data, error } = await supabase.auth.getUser(); - if (!user || !supabaseClient) { - return ; + if (error || !data) { + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; } - return ; + return { props: {} }; } diff --git a/app/src/pages/login.tsx b/app/src/pages/login.tsx new file mode 100644 index 0000000..160f567 --- /dev/null +++ b/app/src/pages/login.tsx @@ -0,0 +1,24 @@ +import type { GetServerSidePropsContext } from 'next'; + +import { createClient as createServerClient } from '@/utils/supabase/server-props'; +import LoginForm from '@/components/LoginForm'; + +export default function Login() { + return ; +} + +export async function getServerSideProps(context: GetServerSidePropsContext) { + const supabase = createServerClient(context); + const { data, error } = await supabase.auth.getUser(); + + if (data.user && !error) { + return { + redirect: { + destination: '/', + permanent: false, + }, + }; + } + + return { props: {} }; +} diff --git a/app/src/utils/supabase/component.ts b/app/src/utils/supabase/component.ts new file mode 100644 index 0000000..4aa548b --- /dev/null +++ b/app/src/utils/supabase/component.ts @@ -0,0 +1,10 @@ +import { createBrowserClient } from "@supabase/ssr"; + +export function createClient() { + const supabase = createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + ); + + return supabase; +} diff --git a/app/src/utils/supabase/server-props.ts b/app/src/utils/supabase/server-props.ts new file mode 100644 index 0000000..79d24df --- /dev/null +++ b/app/src/utils/supabase/server-props.ts @@ -0,0 +1,31 @@ +import { + createServerClient, + type CookieOptions, + serialize, +} from "@supabase/ssr"; +import { type GetServerSidePropsContext } from "next"; + +export function createClient(context: GetServerSidePropsContext) { + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + get(name: string) { + return context.req.cookies[name]; + }, + set(name: string, value: string, options: CookieOptions) { + context.res.appendHeader( + "Set-Cookie", + serialize(name, value, options), + ); + }, + remove(name: string, options: CookieOptions) { + context.res.appendHeader("Set-Cookie", serialize(name, "", options)); + }, + }, + }, + ); + + return supabase; +} diff --git a/app/src/utils/useSupabaseConfig.tsx b/app/src/utils/useSupabaseConfig.tsx deleted file mode 100644 index a3c3ef3..0000000 --- a/app/src/utils/useSupabaseConfig.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Preferences } from '@capacitor/preferences'; -import { SupabaseClient, User, createClient } from '@supabase/supabase-js'; -import { useEffect, useState } from 'react'; - -export function useSupabaseConfig() { - const [supabaseUrl, setSupabaseUrl] = useState(''); - const [supabaseToken, setSupabaseToken] = useState(''); - - useEffect(() => { - async function fetchConfig() { - try { - const supabaseUrlValue = await Preferences.get({ key: 'supabaseUrl' }); - const supabaseTokenValue = await Preferences.get({ - key: 'supabaseToken', - }); - - if (supabaseUrlValue.value) { - setSupabaseUrl(JSON.parse(supabaseUrlValue.value)); - } - if (supabaseTokenValue.value) { - setSupabaseToken(JSON.parse(supabaseTokenValue.value)); - } - } catch (error) { - // Handle any error that might occur during fetching - console.error(error); - } - } - - fetchConfig(); - }, []); - - const setSupabaseConfig = async ( - newSupabaseUrl: string, - newSupabaseToken: string - ) => { - try { - await Preferences.set({ - key: 'supabaseUrl', - value: JSON.stringify(newSupabaseUrl), - }); - await Preferences.set({ - key: 'supabaseToken', - value: JSON.stringify(newSupabaseToken), - }); - setSupabaseUrl(newSupabaseUrl); - setSupabaseToken(newSupabaseToken); - } catch (error) { - // Handle any error that might occur during setting - console.error(error); - } - }; - - return { supabaseUrl, supabaseToken, setSupabaseConfig }; -} - -export function useSupabaseClient() { - const { supabaseUrl, supabaseToken } = useSupabaseConfig(); - const [supabaseClient, setSupabaseClient] = useState(); - - useEffect(() => { - if (supabaseUrl && supabaseToken) { - const client = createClient(supabaseUrl, supabaseToken); - setSupabaseClient(client); - } - }, [supabaseUrl, supabaseToken]); - - return supabaseClient; -} - -export function useSupabase() { - const supabaseClient = useSupabaseClient(); - const [user, setUser] = useState(null); - - useEffect(() => { - if (!supabaseClient) return; - - const { data: authListener } = supabaseClient.auth.onAuthStateChange( - async (event, session) => { - setUser(session?.user ?? null); - } - ); - - return () => { - authListener?.subscription.unsubscribe(); - }; - }, [supabaseClient]); - - return { user, supabaseClient }; -} From df18d8e26baeec5f5d69fadee741c6e5efce325b Mon Sep 17 00:00:00 2001 From: Tim Routowicz Date: Sun, 25 Feb 2024 13:06:49 -0500 Subject: [PATCH 2/2] Remove unused deps --- app/package-lock.json | 47 ------------------------------------------- app/package.json | 3 --- 2 files changed, 50 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 8cd31d2..36fee29 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,12 +12,9 @@ "@capacitor/core": "^5.6.0", "@capacitor/ios": "^5.6.0", "@capacitor/preferences": "^5.0.7", - "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-slot": "^1.0.2", - "@supabase/auth-helpers-nextjs": "^0.8.7", - "@supabase/auth-helpers-react": "^0.4.2", "@supabase/ssr": "^0.1.0", "@supabase/supabase-js": "^2.39.3", "@tanstack/react-query": "^5.18.1", @@ -1418,37 +1415,6 @@ "integrity": "sha512-Jh4t/593gxs0lJZ/z3NnasKlplXT2f+4y/LZYuaKZW5KAaiVFL/fThhs+17EbUd53jUVJ0QudYCBGbN/psvaqg==", "dev": true }, - "node_modules/@supabase/auth-helpers-nextjs": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/@supabase/auth-helpers-nextjs/-/auth-helpers-nextjs-0.8.7.tgz", - "integrity": "sha512-iYdOjFo0GkRvha340l8JdCiBiyXQuG9v8jnq7qMJ/2fakrskRgHTCOt7ryWbip1T6BExcWKC8SoJrhCzPOxhhg==", - "dependencies": { - "@supabase/auth-helpers-shared": "0.6.3", - "set-cookie-parser": "^2.6.0" - }, - "peerDependencies": { - "@supabase/supabase-js": "^2.19.0" - } - }, - "node_modules/@supabase/auth-helpers-react": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@supabase/auth-helpers-react/-/auth-helpers-react-0.4.2.tgz", - "integrity": "sha512-zRj1leYMKJVYQeHFvZiUzlmHM+ATWFR/V7Q9F0yXSWEnMcNHL0CKnIBqhkjtSQ2trE+YaoCvFEHjxISppxIZXQ==", - "peerDependencies": { - "@supabase/supabase-js": "^2.19.0" - } - }, - "node_modules/@supabase/auth-helpers-shared": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@supabase/auth-helpers-shared/-/auth-helpers-shared-0.6.3.tgz", - "integrity": "sha512-xYQRLFeFkL4ZfwC7p9VKcarshj3FB2QJMgJPydvOY7J5czJe6xSG5/wM1z63RmAzGbCkKg+dzpq61oeSyWiGBQ==", - "dependencies": { - "jose": "^4.14.4" - }, - "peerDependencies": { - "@supabase/supabase-js": "^2.19.0" - } - }, "node_modules/@supabase/functions-js": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.1.5.tgz", @@ -4210,14 +4176,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/jose": { - "version": "4.15.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", - "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5593,11 +5551,6 @@ "node": ">=10" } }, - "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" - }, "node_modules/set-function-length": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", diff --git a/app/package.json b/app/package.json index f24f3bc..39482bd 100644 --- a/app/package.json +++ b/app/package.json @@ -21,12 +21,9 @@ "@capacitor/core": "^5.6.0", "@capacitor/ios": "^5.6.0", "@capacitor/preferences": "^5.0.7", - "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-slot": "^1.0.2", - "@supabase/auth-helpers-nextjs": "^0.8.7", - "@supabase/auth-helpers-react": "^0.4.2", "@supabase/ssr": "^0.1.0", "@supabase/supabase-js": "^2.39.3", "@tanstack/react-query": "^5.18.1",