Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nextjs 14 supabase ssr #9

Merged
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: 3 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ SUPABASE_DB_PASSWORD=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_1234
STRIPE_SECRET_KEY=sk_test_1234
STRIPE_WEBHOOK_SECRET=whsec_1234

# Add NEXT_PUBLIC_SITE_URL to your Vercel environmental variables for the production environment following the example below. This defaults to http://localhost:3000 in development.
# NEXT_PUBLIC_SITE_URL=https://your-deployment-url.vercel.app
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ yarn-error.log*

# editors
.vscode
*.code-workspace

# certificates
certificates
13 changes: 13 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Build artifacts
.next/
.turbo/
_next/
__tmp__/
dist/
node_modules/
target/
compiled/

pnpm-lock.yaml

types_db.ts
6 changes: 6 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"arrowParens": "always",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none"
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Next, run the following command to start a local Supabase instance and run the m
pnpm run supabase:start
```

The terminal output will provide you with values for the environment variables `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, and `SUPABASE_SERVICE_ROLE_KEY`. Copy these into your `.env.local` file.
The terminal output will provide you with values for the environment variables `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, and `SUPABASE_SERVICE_ROLE_KEY`. Copy these into your `.env.local` file.

The terminal output will also provide you with a URL to access the local Supabase Studio, where you can make changes to your local database instance. (You can always find the Supabase Studio later by opening up a Docker window, navigating to `Containers` tab, and clicking the link in the `Ports` column for the corresponding container.)

Expand Down
8 changes: 3 additions & 5 deletions app/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import CustomerPortalForm from '@/components/ui/AccountForms/CustomerPortalForm'
import EmailForm from '@/components/ui/AccountForms/EmailForm';
import NameForm from '@/components/ui/AccountForms/NameForm';
import { createClient } from '@/utils/supabase/server';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export default async function Account() {
const cookieStore = cookies();
const supabase = createClient(cookieStore);
const supabase = createClient();

const {
data: { user }
} = await supabase.auth.getUser();

const { data: userDetails } = await supabase
.from('users')
.select('*')
Expand Down Expand Up @@ -53,4 +51,4 @@ export default async function Account() {
</div>
</section>
);
};
}
2 changes: 1 addition & 1 deletion app/api/webhooks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export async function POST(req: Request) {
} catch (error) {
console.log(error);
return new Response(
'Webhook handler failed. View your NextJS function logs.',
'Webhook handler failed. View your Next.js function logs.',
{
status: 400
}
Expand Down
20 changes: 13 additions & 7 deletions app/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createClient } from '@/utils/supabase/server';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { getErrorRedirect, getStatusRedirect } from '@/utils/helpers';
Expand All @@ -11,20 +10,27 @@ export async function GET(request: NextRequest) {
const code = requestUrl.searchParams.get('code');

if (code) {
const cookieStore = cookies();
const supabase = createClient(cookieStore);

const supabase = createClient();

const { error } = await supabase.auth.exchangeCodeForSession(code);

if (error) {
return NextResponse.redirect(
getErrorRedirect(`${requestUrl.origin}/signin`, error.name, "Sorry, we weren't able to log you in. Please try again.")
getErrorRedirect(
`${requestUrl.origin}/signin`,
error.name,
"Sorry, we weren't able to log you in. Please try again."
)
);
}
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(
getStatusRedirect(`${requestUrl.origin}/account`, 'Success!', 'You are now signed in.')
getStatusRedirect(
`${requestUrl.origin}/account`,
'Success!',
'You are now signed in.'
)
);
}
24 changes: 14 additions & 10 deletions app/auth/reset_password/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createClient } from '@/utils/supabase/server';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { getErrorRedirect, getStatusRedirect } from '@/utils/helpers';
Expand All @@ -11,22 +10,27 @@ export async function GET(request: NextRequest) {
const code = requestUrl.searchParams.get('code');

if (code) {
const cookieStore = cookies();
const supabase = createClient(cookieStore);

const supabase = createClient();

const { error } = await supabase.auth.exchangeCodeForSession(code);

if (error) {
return NextResponse.redirect(
getErrorRedirect(`${requestUrl.origin}/signin/forgot_password`, error.name,
"Sorry, we weren't able to log you in. Please try again.")
getErrorRedirect(
`${requestUrl.origin}/signin/forgot_password`,
error.name,
"Sorry, we weren't able to log you in. Please try again."
)
);
}
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(
getStatusRedirect(`${requestUrl.origin}/signin/update_password`, 'You are now signed in.',
'Please enter a new password for your account.')
getStatusRedirect(
`${requestUrl.origin}/signin/update_password`,
'You are now signed in.',
'Please enter a new password for your account.'
)
);
}
}
30 changes: 15 additions & 15 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const meta = {
};

export async function generateMetadata(): Promise<Metadata> {
return({
return {
title: meta.title,
description: meta.description,
referrer: 'origin-when-cross-origin',
Expand All @@ -27,7 +27,7 @@ export async function generateMetadata(): Promise<Metadata> {
creator: 'Vercel',
publisher: 'Vercel',
robots: meta.robots,
icons: {icon: meta.favicon},
icons: { icon: meta.favicon },
metadataBase: new URL(meta.url),
openGraph: {
url: meta.url,
Expand All @@ -45,8 +45,8 @@ export async function generateMetadata(): Promise<Metadata> {
description: meta.description,
images: [meta.cardImage]
}
});
};
};
}

export default async function RootLayout({
// Layouts must accept a children prop.
Expand All @@ -56,17 +56,17 @@ export default async function RootLayout({
return (
<html lang="en">
<body className="bg-black loading">
<Navbar />
<main
id="skip"
className="min-h-[calc(100dvh-4rem)] md:min-h[calc(100dvh-5rem)]"
>
{children}
</main>
<Footer />
<Suspense>
<Toaster />
</Suspense>
<Navbar />
<main
id="skip"
className="min-h-[calc(100dvh-4rem)] md:min-h[calc(100dvh-5rem)]"
>
{children}
</main>
<Footer />
<Suspense>
<Toaster />
</Suspense>
</body>
</html>
);
Expand Down
4 changes: 1 addition & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import Pricing from '@/components/ui/Pricing/Pricing';
import { createClient } from '@/utils/supabase/server';
import { cookies } from 'next/headers';

export default async function PricingPage() {
const cookieStore = cookies();
const supabase = createClient(cookieStore);
const supabase = createClient();

const {
data: { user }
Expand Down
86 changes: 60 additions & 26 deletions app/signin/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Logo from '@/components/icons/Logo';
import { createClient } from '@/utils/supabase/server';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import {
import {
getAuthTypes,
getViewTypes,
getDefaultSignInView,
Expand All @@ -19,26 +19,32 @@ import ForgotPassword from '@/components/ui/AuthForms/ForgotPassword';
import UpdatePassword from '@/components/ui/AuthForms/UpdatePassword';
import SignUp from '@/components/ui/AuthForms/Signup';

export default async function SignIn({ params, searchParams }: { params: { id: string }, searchParams: { disable_button: boolean } }) {
const {allowOauth, allowEmail, allowPassword } = getAuthTypes();
export default async function SignIn({
params,
searchParams
}: {
params: { id: string };
searchParams: { disable_button: boolean };
}) {
const { allowOauth, allowEmail, allowPassword } = getAuthTypes();
const viewTypes = getViewTypes();
const redirectMethod = getRedirectMethod();

// Declare 'viewProp' and initialize with the default value
let viewProp: string
let viewProp: string;

// Assign url id to 'viewProp' if it's a valid string and ViewTypes includes it
if (typeof params.id === 'string' && viewTypes.includes(params.id)) {
viewProp = params.id;
} else {
const preferredSignInView = cookies().get('preferredSignInView')?.value || null;
const preferredSignInView =
cookies().get('preferredSignInView')?.value || null;
viewProp = getDefaultSignInView(preferredSignInView);
return redirect(`/signin/${viewProp}`);
}

// Check if the user is already logged in and redirect to the account page if so
const cookieStore = cookies();
const supabase = createClient(cookieStore);
const supabase = createClient();
const {
data: { session }
} = await supabase.auth.getSession();
Expand All @@ -55,25 +61,53 @@ export default async function SignIn({ params, searchParams }: { params: { id:
<div className="flex justify-center pb-12 ">
<Logo width="64px" height="64px" />
</div>
<Card title={
viewProp === 'forgot_password' ? 'Reset Password' :
viewProp === 'update_password' ? 'Update Password' :
viewProp === 'signup' ? 'Sign Up' :
'Sign In'
}>
{viewProp === 'password_signin' && <PasswordSignIn allowEmail={allowEmail} redirectMethod={redirectMethod} />}
{viewProp === 'email_signin' && <EmailSignIn allowPassword={allowPassword} redirectMethod={redirectMethod} disableButton={ searchParams.disable_button} />}
{viewProp === 'forgot_password' && <ForgotPassword allowEmail={allowEmail} redirectMethod={redirectMethod} disableButton={ searchParams.disable_button} />}
{viewProp === 'update_password' && <UpdatePassword redirectMethod={redirectMethod} />}
{viewProp === 'signup' && <SignUp allowEmail={allowEmail} redirectMethod={redirectMethod} />}
{viewProp !== 'update_password' && viewProp !== 'signup' && allowOauth && (
<>
<Separator text="Third-party sign-in" />
<OauthSignIn />
</>
<Card
title={
viewProp === 'forgot_password'
? 'Reset Password'
: viewProp === 'update_password'
? 'Update Password'
: viewProp === 'signup'
? 'Sign Up'
: 'Sign In'
}
>
{viewProp === 'password_signin' && (
<PasswordSignIn
allowEmail={allowEmail}
redirectMethod={redirectMethod}
/>
)}
{viewProp === 'email_signin' && (
<EmailSignIn
allowPassword={allowPassword}
redirectMethod={redirectMethod}
disableButton={searchParams.disable_button}
/>
)}
{viewProp === 'forgot_password' && (
<ForgotPassword
allowEmail={allowEmail}
redirectMethod={redirectMethod}
disableButton={searchParams.disable_button}
/>
)}
{viewProp === 'update_password' && (
<UpdatePassword redirectMethod={redirectMethod} />
)}
{viewProp === 'signup' && (
<SignUp allowEmail={allowEmail} redirectMethod={redirectMethod} />
)}
{viewProp !== 'update_password' &&
viewProp !== 'signup' &&
allowOauth && (
<>
<Separator text="Third-party sign-in" />
<OauthSignIn />
</>
)}
</Card>
</div>
</div>
);
}
}
9 changes: 5 additions & 4 deletions app/signin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { redirect } from 'next/navigation';
import { getDefaultSignInView } from '@/utils/auth-helpers/settings';
import { cookies } from 'next/headers';

export default async function SignIn() {
const preferredSignInView = cookies().get('preferredSignInView')?.value || null;
export default function SignIn() {
const preferredSignInView =
cookies().get('preferredSignInView')?.value || null;
const defaultView = getDefaultSignInView(preferredSignInView);

return redirect(`/signin/${defaultView}`);
};
}
Loading