source: server/authjs-handler.ts

main
Last change on this file was 6270fa4, checked in by Tome <gjorgievtome@…>, 5 months ago

Trim whitespace from username and email during authentication and registration

  • Property mode set to 100644
File size: 4.3 KB
RevLine 
[8ee4553]1import { Auth, type AuthConfig, createActionURL, setEnvDefaults } from "@auth/core";
2import CredentialsProvider from "@auth/core/providers/credentials";
3import type { Session } from "@auth/core/types";
4import { enhance, type UniversalHandler, type UniversalMiddleware } from "@universal-middleware/core";
[898cf68]5import {eq, or} from "drizzle-orm";
[66bf4fd]6import { db } from "../database/drizzle/db";
[898cf68]7import {adminsTable, usersTable} from "../database/drizzle/schema";
[66bf4fd]8import bcrypt from "bcrypt";
[898cf68]9import { AuthDrizzleAdapter } from "./drizzle/auth-adapter";
[8ee4553]10
11const authjsConfig = {
12 basePath: "/api/auth",
13 trustHost: true,
[66bf4fd]14 secret: process.env.AUTH_SECRET,
[898cf68]15 adapter: AuthDrizzleAdapter(),
[66bf4fd]16 session: {
17 strategy: "jwt",
18 },
[8ee4553]19 providers: [
20 CredentialsProvider({
21 name: "Credentials",
22 credentials: {
[66bf4fd]23 username: { label: "Username", type: "text" },
[8ee4553]24 password: { label: "Password", type: "password" },
25 },
[66bf4fd]26 async authorize(credentials) {
27 const username = typeof credentials?.username === "string" ? credentials.username : null;
28 const password = typeof credentials?.password === "string" ? credentials.password : null;
29
[898cf68]30 if(!username || !password || typeof username !== 'string' || typeof password !== 'string') { return null; }
[6270fa4]31
32 const trimmedUsername = username.trim();
[66bf4fd]33
34 const user = await db.query.usersTable.findFirst({
[898cf68]35 where: or(
[6270fa4]36 eq(usersTable.username, trimmedUsername),
37 eq(usersTable.email, trimmedUsername)
[898cf68]38 ),
39 });
[66bf4fd]40
[898cf68]41 if(!user) return null;
[66bf4fd]42
[898cf68]43 let isPasswordValid = false;
[8ee4553]44
[898cf68]45 if (user.passwordHash.startsWith('$2b$') || user.passwordHash.startsWith('$2a$')) {
46 isPasswordValid = await bcrypt.compare(password, user.passwordHash);
47 } else {
48 isPasswordValid = password === user.passwordHash;
49 }
50
51 if(!isPasswordValid) return null;
52
53 const admin = await db.query.adminsTable.findFirst({
54 where: eq(adminsTable.userId, user.id),
55 });
[66bf4fd]56
57 return {
58 id: String(user.id),
[898cf68]59 username: user.username,
[66bf4fd]60 email: user.email,
[898cf68]61 isAdmin: !!admin
[66bf4fd]62 }
[8ee4553]63 },
64 }),
65 ],
[66bf4fd]66
67 callbacks: {
68 async jwt({ token, user }) {
[898cf68]69 if (user) {
70 token.sub = user.id;
[016481f]71
[898cf68]72 const customUser = user as any;
[ad311c3]73 token.username = customUser.username;
[898cf68]74 token.email = customUser.email;
75 token.isAdmin = customUser.isAdmin;
76 }
[66bf4fd]77 return token;
78 },
79 async session({ session, token }) {
[898cf68]80 if (session.user) {
81 session.user.id = token.sub!;
[ad311c3]82 session.user.name = token.username as string;
[898cf68]83
84 const customToken = token as any;
85 session.user.email = customToken.email;
86 session.user.isAdmin = customToken.isAdmin;
87 }
[66bf4fd]88 return session;
89 },
90 },
[8ee4553]91} satisfies Omit<AuthConfig, "raw">;
92
93export async function getSession(req: Request, config: Omit<AuthConfig, "raw">): Promise<Session | null> {
94 setEnvDefaults(process.env, config);
95 const requestURL = new URL(req.url);
96 const url = createActionURL("session", requestURL.protocol, req.headers, process.env, config);
97
98 const response = await Auth(new Request(url, { headers: { cookie: req.headers.get("cookie") ?? "" } }), config);
99
100 const { status = 200 } = response;
101
102 const data = await response.json();
103
104 if (!data || !Object.keys(data).length) return null;
105 if (status === 200) return data as Session;
106 throw new Error(typeof data === "object" && "message" in data ? (data.message as string) : undefined);
107}
108
109export const authjsSessionMiddleware: UniversalMiddleware = enhance(
110 async (request, context) => {
111 try {
112 return {
113 ...context,
114 session: await getSession(request, authjsConfig),
115 };
116 } catch (error) {
117 console.debug("authjsSessionMiddleware:", error);
118 return {
119 ...context,
120 session: null,
121 };
122 }
123 },
124 {
[66bf4fd]125 name: "pc-forge:authjs-middleware",
[8ee4553]126 immutable: false,
127 },
128);
129
130export const authjsHandler = enhance(
131 async (request) => {
132 return Auth(request, authjsConfig);
133 },
134 {
[66bf4fd]135 name: "pc-forge:authjs-handler",
[8ee4553]136 path: "/api/auth/**",
137 method: ["GET", "POST"],
138 immutable: false,
139 },
140) satisfies UniversalHandler;
Note: See TracBrowser for help on using the repository browser.