diff --git a/apps/web/.env.example b/apps/web/.env.example
index b321f09a..f27da946 100644
--- a/apps/web/.env.example
+++ b/apps/web/.env.example
@@ -7,6 +7,7 @@ NODE_ENV=development
# Public
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_MARBLE_API_URL=https://api.marblecms.com
+NEXT_PUBLIC_AUTH_API_BASE_URL=http://localhost:3001/api
# Server
DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut"
@@ -25,4 +26,4 @@ R2_ACCESS_KEY_ID=your_access_key_here
R2_SECRET_ACCESS_KEY=your_secret_key_here
R2_BUCKET_NAME=opencut-transcription # whatever you named your r2 bucket
-MODAL_TRANSCRIPTION_URL=your_modal_url_here
\ No newline at end of file
+MODAL_TRANSCRIPTION_URL=your_modal_url_here
diff --git a/apps/web/src/app/auth/login/page.tsx b/apps/web/src/app/auth/login/page.tsx
new file mode 100644
index 00000000..5bd1e186
--- /dev/null
+++ b/apps/web/src/app/auth/login/page.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import Link from "next/link";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useEffect, useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { useAuthStore } from "@/stores/auth-store";
+
+export default function LoginPage() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const nextPath = searchParams.get("next") || "/projects";
+
+ const login = useAuthStore((state) => state.login);
+ const clearError = useAuthStore((state) => state.clearError);
+ const error = useAuthStore((state) => state.error);
+ const status = useAuthStore((state) => state.status);
+ const isInitialized = useAuthStore((state) => state.isInitialized);
+
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+
+ useEffect(() => {
+ if (status === "authenticated") {
+ router.replace(nextPath);
+ }
+ }, [nextPath, router, status]);
+
+ const isSubmitting = status === "loading";
+
+ return (
+
+
+
+ Sign in
+
+
+ {error ? (
+
+ {error}
+
+ ) : null}
+
+
+ No account yet?{" "}
+
+ Create one
+
+
+ {!isInitialized ? (
+ Loading session...
+ ) : null}
+
+
+
+ );
+}
diff --git a/apps/web/src/app/auth/register/page.tsx b/apps/web/src/app/auth/register/page.tsx
new file mode 100644
index 00000000..f39704ae
--- /dev/null
+++ b/apps/web/src/app/auth/register/page.tsx
@@ -0,0 +1,131 @@
+"use client";
+
+import Link from "next/link";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useEffect, useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { useAuthStore } from "@/stores/auth-store";
+
+export default function RegisterPage() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const nextPath = searchParams.get("next") || "/projects";
+
+ const register = useAuthStore((state) => state.register);
+ const clearError = useAuthStore((state) => state.clearError);
+ const error = useAuthStore((state) => state.error);
+ const status = useAuthStore((state) => state.status);
+
+ const [firstname, setFirstname] = useState("");
+ const [lastname, setLastname] = useState("");
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [organisationName, setOrganisationName] = useState("");
+
+ useEffect(() => {
+ if (status === "authenticated") {
+ router.replace(nextPath);
+ }
+ }, [nextPath, router, status]);
+
+ const isSubmitting = status === "loading";
+
+ return (
+
+
+
+ Create account
+
+
+ {error ? (
+
+ {error}
+
+ ) : null}
+
+
+ Already have an account?{" "}
+
+ Sign in
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 2b3d0c30..e036236d 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -7,6 +7,8 @@ import { baseMetaData } from "./metadata";
import { BotIdClient } from "botid/client";
import { webEnv } from "@opencut/env/web";
import { Inter } from "next/font/google";
+import { AuthProvider } from "@/components/providers/auth-provider";
+import { AuthGuard } from "@/components/auth/auth-guard";
const siteFont = Inter({ subsets: ["latin"] });
@@ -35,22 +37,24 @@ export default function RootLayout({
defaultTheme="system"
disableTransitionOnChange={true}
>
-
-
-
- {children}
-
+
+
+
+
+ {children}
+
+