Skip to content

Commit

Permalink
fix: nextjs email auth flow
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorpfiz committed Aug 1, 2024
1 parent 7f4772c commit 0ce03f4
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 67 deletions.
5 changes: 3 additions & 2 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
"with-env": "dotenv -e ../../.env --"
},
"dependencies": {
"@supabase/ssr": "catalog:supabase",
"@supabase/supabase-js": "catalog:supabase",
"@hyper/api": "workspace:*",
"@hyper/db": "workspace:*",
"@hyper/ui": "workspace:*",
"@hyper/validators": "workspace:*",
"@supabase/ssr": "catalog:supabase",
"@supabase/supabase-js": "catalog:supabase",
"@t3-oss/env-nextjs": "^0.11.0",
"@tanstack/react-query": "catalog:",
"@trpc/client": "catalog:",
Expand All @@ -35,6 +35,7 @@
"novel": "^0.5.0",
"react": "catalog:react18",
"react-dom": "catalog:react18",
"react-icons": "^5.2.1",
"superjson": "catalog:",
"use-debounce": "^10.0.2",
"zod": "catalog:"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
import { createClient } from "~/utils/supabase/server";

export async function GET(request: NextRequest) {
const { searchParams, origin } = new URL(request.url);
const { searchParams } = new URL(request.url);
const code = searchParams.get("code");
// if "next" is in param, use it as the redirect URL
const next = searchParams.get("next") ?? "/";
Expand All @@ -14,10 +14,10 @@ export async function GET(request: NextRequest) {

const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return redirect(`${origin}${next}`);
return redirect(next);
}
}

// return the user to an error page with instructions
return redirect(`${origin}/auth/auth-code-error`);
return redirect("/auth/error");
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ export async function GET(request: NextRequest) {
}

// redirect the user to an error page with some instructions
redirect("/error");
redirect("/auth/error");
}
3 changes: 3 additions & 0 deletions apps/nextjs/src/app/(auth)/auth/error/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function ErrorPage() {
return <p>Sorry, something went wrong. Please try again.</p>;
}
19 changes: 19 additions & 0 deletions apps/nextjs/src/app/(auth)/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CardWrapper } from "~/components/auth/card-wrapper";
import { SignInForm } from "~/components/auth/sign-in-form";

export default function SignInPage() {
return (
<main>
<CardWrapper
headerLabel="Welcome back"
backButtonLabel="Don't have an account?"
backButtonLinkLabel="Sign up"
backButtonHref="/signup"
showSocial
showCredentials
>
<SignInForm />
</CardWrapper>
</main>
);
}
19 changes: 19 additions & 0 deletions apps/nextjs/src/app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CardWrapper } from "~/components/auth/card-wrapper";
import { SignUpForm } from "~/components/auth/sign-up-form";

export default function SignUpPage() {
return (
<main>
<CardWrapper
headerLabel="Create your account"
backButtonLabel="Have an account?"
backButtonLinkLabel="Sign in"
backButtonHref="/signin"
showSocial
showCredentials
>
<SignUpForm />
</CardWrapper>
</main>
);
}
2 changes: 1 addition & 1 deletion apps/nextjs/src/components/auth/back-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const BackButton = ({ label, linkLabel, href }: BackButtonProps) => {
return (
<div className="flex w-full flex-row items-center gap-1">
<span className="text-sm text-muted-foreground">{label}</span>
<Link href={href} className="text-sm text-blue-600 hover:underline">
<Link href={href} className="text-sm underline">
{linkLabel}
</Link>
</div>
Expand Down
27 changes: 16 additions & 11 deletions apps/nextjs/src/components/auth/sign-in-form.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use client";

import Link from "next/link";
import { useAction } from "next-safe-action/hooks";

import type { SignInSchemaType } from "@hyper/validators";
import type { SignIn } from "@hyper/validators";
import { Button } from "@hyper/ui/button";
import {
Form,
Expand All @@ -28,9 +29,9 @@ export const SignInForm = () => {
},
});

const { execute, result, status } = useAction(signInWithPassword);
const { execute, result, isExecuting } = useAction(signInWithPassword);

const onSubmit = (values: SignInSchemaType) => {
const onSubmit = (values: SignIn) => {
execute(values);
};

Expand All @@ -47,7 +48,7 @@ export const SignInForm = () => {
<FormControl>
<Input
{...field}
disabled={status === "executing"}
disabled={isExecuting}
placeholder="Email address"
type="email"
/>
Expand All @@ -62,11 +63,19 @@ export const SignInForm = () => {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<div className="flex items-center pt-1">
<FormLabel className="pb-1">Password</FormLabel>
<Link
href="#"
className="ml-auto inline-block text-sm underline"
>
Forgot your password?
</Link>
</div>
<FormControl>
<Input
{...field}
disabled={status === "executing"}
disabled={isExecuting}
placeholder="Password"
type="password"
/>
Expand All @@ -79,11 +88,7 @@ export const SignInForm = () => {

<FormError message={result.serverError} />

<Button
disabled={status === "executing"}
type="submit"
className="w-full"
>
<Button disabled={isExecuting} type="submit" className="w-full">
Continue with Email
</Button>
</form>
Expand Down
18 changes: 7 additions & 11 deletions apps/nextjs/src/components/auth/sign-up-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useAction } from "next-safe-action/hooks";

import type { SignUpSchemaType } from "@hyper/validators";
import type { SignUp } from "@hyper/validators";
import { Button } from "@hyper/ui/button";
import {
Form,
Expand All @@ -29,9 +29,9 @@ export const SignUpForm = () => {
},
});

const { execute, result, status } = useAction(signUp);
const { execute, result, isExecuting, hasSucceeded } = useAction(signUp);

const onSubmit = (values: SignUpSchemaType) => {
const onSubmit = (values: SignUp) => {
execute(values);
};

Expand All @@ -48,7 +48,7 @@ export const SignUpForm = () => {
<FormControl>
<Input
{...field}
disabled={status === "executing"}
disabled={isExecuting}
placeholder="Email address"
type="email"
/>
Expand All @@ -67,7 +67,7 @@ export const SignUpForm = () => {
<FormControl>
<Input
{...field}
disabled={status === "executing"}
disabled={isExecuting}
placeholder="Password"
type="password"
/>
Expand All @@ -78,16 +78,12 @@ export const SignUpForm = () => {
/>
</div>

{status === "hasSucceeded" && (
{hasSucceeded && (
<FormSuccess message={"Confirmation email has been sent!"} />
)}
<FormError message={result.serverError} />

<Button
disabled={status === "executing"}
type="submit"
className="w-full"
>
<Button disabled={isExecuting} type="submit" className="w-full">
Continue with Email
</Button>
</form>
Expand Down
9 changes: 6 additions & 3 deletions apps/nextjs/src/lib/actions/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const signInWithPassword = actionClient
}

revalidatePath("/", "layout");
redirect(DEFAULT_LOGIN_REDIRECT);
// redirect(DEFAULT_LOGIN_REDIRECT);
});

export const signUp = actionClient
Expand Down Expand Up @@ -53,6 +53,7 @@ export const signUp = actionClient
throw error;
}

revalidatePath("/", "layout");
return data.user;
});

Expand All @@ -69,7 +70,8 @@ export const signInWithGithub = async () => {
redirect(res.data.url);
}
if (res.error) {
throw new Error(res.error.message);
console.error(res.error.message);
redirect("/auth/error");
}
};

Expand All @@ -88,7 +90,8 @@ export const signInWithGoogle = async () => {
redirect(res.data.url);
}
if (res.error) {
throw new Error(res.error.message);
console.error(res.error.message);
redirect("/auth/error");
}
};

Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/src/lib/safe-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ export const authActionClient = actionClient.use(async ({ next, ctx }) => {
throw new Error("Unauthorized");
}

return next({ ctx: { user: data.user } });
return next({ ctx: { supabase, user: data.user } });
});
31 changes: 31 additions & 0 deletions packages/db/migrations/0000_overjoyed_cable.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
CREATE TABLE IF NOT EXISTS "auth"."users" (
"id" uuid PRIMARY KEY NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "hyper_profile" (
"id" uuid PRIMARY KEY NOT NULL,
"name" varchar(256) NOT NULL,
"image" varchar(256),
"email" varchar(256)
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "hyper_report" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"title" varchar(256) NOT NULL,
"content" text,
"profile_id" uuid NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updatedAt" timestamp with time zone
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "hyper_profile" ADD CONSTRAINT "hyper_profile_id_users_id_fk" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "hyper_report" ADD CONSTRAINT "hyper_report_profile_id_hyper_profile_id_fk" FOREIGN KEY ("profile_id") REFERENCES "public"."hyper_profile"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
Loading

0 comments on commit 0ce03f4

Please sign in to comment.