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

Remove lucia and setup session authentication from scratch #83

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9e4d7ec
adapters
iamtouha Nov 23, 2024
9b30a1b
wip
iamtouha Nov 23, 2024
d1a4af4
wip
iamtouha Nov 24, 2024
1306e0a
replace lucia on actions
iamtouha Nov 24, 2024
02dcfd0
Refactor authentication imports and session handling across multiple …
iamtouha Nov 24, 2024
f0a21ad
remove lucia dependency
iamtouha Nov 24, 2024
d610482
wip
iamtouha Dec 23, 2024
8b71a5f
dependency update
iamtouha Jan 6, 2025
03f6de5
Refactor login component and implement new TextInput component for be…
iamtouha Jan 7, 2025
79f23a0
update dependencies and refactor login/signup actions
iamtouha Jan 7, 2025
740b46d
Refactor
iamtouha Jan 18, 2025
6a528d5
Merge branch 'main' into feat/remove-lucia
iamtouha Jan 18, 2025
ce9fa3d
Refactor authentication actions
iamtouha Jan 18, 2025
bf7458b
fix lint issues
iamtouha Jan 18, 2025
b660350
migrate broken components
iamtouha Jan 18, 2025
9dddee5
server component related fixes
iamtouha Jan 18, 2025
f2eeaf8
Merge branch 'main' into feat/remove-lucia
iamtouha Jan 20, 2025
1198d02
cleanup
iamtouha Jan 20, 2025
2824c96
Update devcontainer configuration for Next.js development environment
iamtouha Jan 20, 2025
ed60ff5
Refactor: Update icon imports and improve accessibility in user dropd…
iamtouha Jan 20, 2025
47ea84e
next version update
iamtouha Jan 20, 2025
e75c754
Refactor: Improve user dropdown rendering and update dashboard layout…
iamtouha Jan 20, 2025
49f91bb
update Discord authentication logic to use new utility functions and …
iamtouha Jan 20, 2025
9f07467
update ci
iamtouha Jan 20, 2025
9ef1129
update authentication actions
iamtouha Jan 20, 2025
981455c
update success state checks in authentication components for clearer …
iamtouha Jan 21, 2025
6195ee8
simplify post creation and update logic; streamline column selection …
iamtouha Jan 21, 2025
6adcfe9
update footer links to reflect new repository and organization; adjus…
iamtouha Jan 21, 2025
dfb656e
schema update wip
iamtouha Jan 21, 2025
b70f8d0
update
iamtouha Jan 21, 2025
cadb078
simplify post status definition
iamtouha Jan 21, 2025
8c88df0
update
iamtouha Jan 21, 2025
88fa666
limit user posts to a maximum of 5; update schema for consistency and…
iamtouha Jan 21, 2025
88b203a
fix
iamtouha Jan 21, 2025
cbff8ff
docs wip
iamtouha Jan 21, 2025
9f15aab
wip
iamtouha Jan 22, 2025
d393d03
wip
iamtouha Jan 22, 2025
d8f4183
refactor(auth): simplify adapter interface and improve session handling
iamtouha Jan 23, 2025
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
Prev Previous commit
Next Next commit
Refactor authentication imports and session handling across multiple …
…files
iamtouha committed Nov 24, 2024
commit 02dcfd0d690dd9fc1b9a0580b5c65b247bcf61f9
25 changes: 12 additions & 13 deletions src/app/(auth)/login/discord/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { cookies } from "next/headers";
import { generateId } from "lucia";
import { OAuth2RequestError } from "arctic";
import { eq } from "drizzle-orm";
import { discord, lucia } from "@/lib/auth";
import { db } from "@/server/db";
import { discord } from "@/lib/auth";
import utils from "@/lib/auth/utils";
import { Paths } from "@/lib/constants";
import { db } from "@/server/db";
import { users } from "@/server/db/schema";
import { OAuth2RequestError } from "arctic";
import { eq } from "drizzle-orm";
import { cookies } from "next/headers";

export async function GET(request: Request): Promise<Response> {
const url = new URL(request.url);
@@ -48,17 +48,16 @@ export async function GET(request: Request): Promise<Response> {
: null;

if (!existingUser) {
const userId = generateId(21);
const userId = utils.generateId(21);
await db.insert(users).values({
id: userId,
email: discordUser.email,
emailVerified: true,
discordId: discordUser.id,
avatar,
});
const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
const session = await utils.createSession(userId);
utils.setCookie(session.id);
return new Response(null, {
status: 302,
headers: { Location: Paths.Dashboard },
@@ -75,9 +74,9 @@ export async function GET(request: Request): Promise<Response> {
})
.where(eq(users.id, existingUser.id));
}
const session = await lucia.createSession(existingUser.id, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
const session = await utils.createSession(existingUser.id);
utils.setCookie(session.id);

return new Response(null, {
status: 302,
headers: { Location: Paths.Dashboard },
4 changes: 2 additions & 2 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { redirect } from "next/navigation";
import { validateRequest } from "@/lib/auth/validate-request";
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { redirect } from "next/navigation";
import { Login } from "./login";

export const metadata = {
16 changes: 4 additions & 12 deletions src/app/(auth)/reset-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { redirect } from "next/navigation";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { SendResetEmail } from "./send-reset-email";
import { validateRequest } from "@/lib/auth/validate-request";
import { Paths } from "@/lib/constants";

export const metadata = {
title: "Forgot Password",
@@ -24,9 +18,7 @@ export default async function ForgotPasswordPage() {
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>Forgot password?</CardTitle>
<CardDescription>
Password reset link will be sent to your email.
</CardDescription>
<CardDescription>Password reset link will be sent to your email.</CardDescription>
</CardHeader>
<CardContent>
<SendResetEmail />
4 changes: 2 additions & 2 deletions src/app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { redirect } from "next/navigation";
import { Signup } from "./signup";
import { validateRequest } from "@/lib/auth/validate-request";
import { Paths } from "@/lib/constants";

export const metadata = {
title: "Sign Up",
16 changes: 5 additions & 11 deletions src/app/(auth)/verify-email/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { redirect } from "next/navigation";
import { validateRequest } from "@/lib/auth/validate-request";
import { VerifyCode } from "./verify-code";
import { Paths } from "@/lib/constants";

export const metadata = {
title: "Verify Email",
@@ -26,8 +20,8 @@ export default async function VerifyEmailPage() {
<CardHeader>
<CardTitle>Verify Email</CardTitle>
<CardDescription>
Verification code was sent to <strong>{user.email}</strong>. Check
your spam folder if you can't find the email.
Verification code was sent to <strong>{user.email}</strong>. Check your spam folder if you
can't find the email.
</CardDescription>
</CardHeader>
<CardContent>
6 changes: 3 additions & 3 deletions src/app/(main)/_components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Link from "next/link";
import { UserDropdown } from "@/app/(main)/_components/user-dropdown";
import { RocketIcon } from "@/components/icons";
import { validateRequest } from "@/lib/auth";
import { APP_TITLE } from "@/lib/constants";
import { UserDropdown } from "@/app/(main)/_components/user-dropdown";
import { validateRequest } from "@/lib/auth/validate-request";
import Link from "next/link";

export const Header = async () => {
const { user } = await validateRequest();
4 changes: 2 additions & 2 deletions src/app/(main)/account/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { redirect } from "next/navigation";
import { SubmitButton } from "@/components/submit-button";
import {
Card,
@@ -8,9 +7,10 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { validateRequest } from "@/lib/auth";
import { logout } from "@/lib/auth/actions";
import { validateRequest } from "@/lib/auth/validate-request";
import { Paths } from "@/lib/constants";
import { redirect } from "next/navigation";

export default async function AccountPage() {
const { user } = await validateRequest();
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import { ExclamationTriangleIcon } from "@/components/icons";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { validateRequest } from "@/lib/auth/validate-request";
import { validateRequest } from "@/lib/auth";
import Link from "next/link";

export async function VerificiationWarning() {
2 changes: 1 addition & 1 deletion src/app/(main)/dashboard/billing/page.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { ExclamationTriangleIcon } from "@/components/icons";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { env } from "@/env";
import { validateRequest } from "@/lib/auth/validate-request";
import { validateRequest } from "@/lib/auth";
import { APP_TITLE } from "@/lib/constants";
import { api } from "@/trpc/server";
import * as React from "react";
8 changes: 4 additions & 4 deletions src/app/(main)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { redirect } from "next/navigation";
import { env } from "@/env";
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { myPostsSchema } from "@/server/api/routers/post/post.input";
import { api } from "@/trpc/server";
import { type Metadata } from "next";
import { redirect } from "next/navigation";
import * as React from "react";
import { Posts } from "./_components/posts";
import { PostsSkeleton } from "./_components/posts-skeleton";
import { validateRequest } from "@/lib/auth/validate-request";
import { Paths } from "@/lib/constants";
import { myPostsSchema } from "@/server/api/routers/post/post.input";

export const metadata: Metadata = {
metadataBase: new URL(env.NEXT_PUBLIC_APP_URL),
2 changes: 1 addition & 1 deletion src/app/(main)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
import { redirect } from "next/navigation";

import { env } from "@/env";
import { validateRequest } from "@/lib/auth/validate-request";
import { validateRequest } from "@/lib/auth";

export const metadata: Metadata = {
metadataBase: new URL(env.NEXT_PUBLIC_APP_URL),
9 changes: 4 additions & 5 deletions src/app/(main)/editor/[postId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from "react";
import { ArrowLeftIcon } from "@/components/icons";
import { validateRequest } from "@/lib/auth";
import { Paths } from "@/lib/constants";
import { api } from "@/trpc/server";
import Link from "next/link";
import { notFound, redirect } from "next/navigation";
import { PostEditor } from "./_components/post-editor";
import { ArrowLeftIcon } from "@/components/icons";
import Link from "next/link";
import { validateRequest } from "@/lib/auth/validate-request";
import { Paths } from "@/lib/constants";

interface Props {
params: {
9 changes: 6 additions & 3 deletions src/lib/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Discord } from "arctic";
import { env } from "@/env.js";
import { absoluteUrl } from "@/lib/utils";
import { TimeSpan } from "./utils";
import { Discord } from "arctic";
import { cache } from "react";
import utils, { TimeSpan } from "./utils";

export const discord = new Discord(
env.DISCORD_CLIENT_ID,
@@ -13,7 +14,9 @@ export const sessionExpiration = new TimeSpan(30, "d");
export const sessionCookieName = "auth_session";
export const sessionCookieOptions = {
httpOnly: true,
secure: true,
secure: env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
} as const;

export const validateRequest = cache(utils.validateRequest);
30 changes: 30 additions & 0 deletions src/lib/auth/utils.ts
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ const utils = {
verifyPassword,
isWithinExpirationDate,
createTimeSpanDate,
verifyRequestOrigin,
};
export default utils;

@@ -185,3 +186,32 @@ function generateScryptKey(str: string, salt: string, blockSize = 16) {
);
});
}
function verifyRequestOrigin(origin: string, allowedDomains: string[]): boolean {
if (!origin || allowedDomains.length === 0) {
return false;
}
const originHost = safeURL(origin)?.host ?? null;
if (!originHost) {
return false;
}
for (const domain of allowedDomains) {
let host: string | null;
if (domain.startsWith("http://") || domain.startsWith("https://")) {
host = safeURL(domain)?.host ?? null;
} else {
host = safeURL("https://" + domain)?.host ?? null;
}
if (originHost === host) {
return true;
}
}
return false;
}

function safeURL(url: URL | string): URL | null {
try {
return new URL(url);
} catch {
return null;
}
}
14 changes: 4 additions & 10 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
// middleware.ts
import { verifyRequestOrigin } from "lucia";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import utils from "./lib/auth/utils";

export async function middleware(request: NextRequest): Promise<NextResponse> {
if (request.method === "GET") {
return NextResponse.next();
}
const originHeader = request.headers.get("Origin");
const hostHeader = request.headers.get("Host");
if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader])
) {
if (!originHeader || !hostHeader || !utils.verifyRequestOrigin(originHeader, [hostHeader])) {
return new NextResponse(null, {
status: 403,
});
@@ -22,7 +18,5 @@ export async function middleware(request: NextRequest): Promise<NextResponse> {
}

export const config = {
matcher: [
"/((?!api|static|.*\\..*|_next|favicon.ico|sitemap.xml|robots.txt).*)",
],
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|sitemap.xml|robots.txt).*)"],
};
8 changes: 4 additions & 4 deletions src/server/api/routers/post/post.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { generateId } from "lucia";
import utils from "@/lib/auth/utils";
import { posts } from "@/server/db/schema";
import { eq } from "drizzle-orm";
import type { ProtectedTRPCContext } from "../../trpc";
import type {
CreatePostInput,
@@ -8,8 +10,6 @@ import type {
MyPostsInput,
UpdatePostInput,
} from "./post.input";
import { posts } from "@/server/db/schema";
import { eq } from "drizzle-orm";

export const listPosts = async (ctx: ProtectedTRPCContext, input: ListPostsInput) => {
return ctx.db.query.posts.findMany({
@@ -36,7 +36,7 @@ export const getPost = async (ctx: ProtectedTRPCContext, { id }: GetPostInput) =
};

export const createPost = async (ctx: ProtectedTRPCContext, input: CreatePostInput) => {
const id = generateId(15);
const id = utils.generateId(15);

await ctx.db.insert(posts).values({
id,
4 changes: 2 additions & 2 deletions src/server/api/trpc.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
* need to use are documented accordingly near the end.
*/

import { uncachedValidateRequest } from "@/lib/auth/validate-request";
import utils from "@/lib/auth/utils";
import { stripe } from "@/lib/stripe";
import { db } from "@/server/db";
import { initTRPC, TRPCError, type inferAsyncReturnType } from "@trpc/server";
@@ -27,7 +27,7 @@ import { ZodError } from "zod";
* @see https://trpc.io/docs/server/context
*/
export const createTRPCContext = async (opts: { headers: Headers }) => {
const { session, user } = await uncachedValidateRequest();
const { session, user } = await utils.validateRequest();
return {
session,
user,