Changes between Initial Version and Version 1 of Security


Ignore:
Timestamp:
01/29/26 02:17:55 (11 days ago)
Author:
233051
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Security

    v1 v1  
     1== Security ==
     2
     3{{{
     4const authjsConfig = {
     5  basePath: "/api/auth",
     6  trustHost: true,
     7  secret: process.env.AUTH_SECRET,
     8  adapter: AuthDrizzleAdapter(),
     9  session: {
     10    strategy: "jwt",
     11  },
     12  providers: [
     13    CredentialsProvider({
     14      name: "Credentials",
     15      credentials: {
     16        username: { label: "Username", type: "text" },
     17        password: { label: "Password", type: "password" },
     18      },
     19      async authorize(credentials) {
     20        const username = typeof credentials?.username === "string" ? credentials.username : null;
     21        const password = typeof credentials?.password === "string" ? credentials.password : null;
     22
     23        if(!username || !password || typeof username !== 'string' || typeof password !== 'string') { return null; }
     24
     25        const user = await db.query.usersTable.findFirst({
     26            where: or(
     27                eq(usersTable.username, username),
     28                eq(usersTable.email, username)
     29            ),
     30          });
     31
     32        if(!user) return null;
     33
     34        const isPasswordValid = await bcrypt.compare(password, user.passwordHash);
     35
     36        if(!isPasswordValid) return null;
     37
     38          const admin = await db.query.adminsTable.findFirst({
     39              where: eq(adminsTable.userId, user.id),
     40          });
     41
     42        return {
     43            id: String(user.id),
     44            username: user.username,
     45            email: user.email,
     46            isAdmin: !!admin
     47        }
     48      },
     49    }),
     50  ],
     51
     52  callbacks: {
     53      async jwt({ token, user }) {
     54          if (user) {
     55              token.sub = user.id;
     56
     57              const customUser = user as any;
     58              token.username = customUser.username;
     59              token.email = customUser.email;
     60              token.isAdmin = customUser.isAdmin;
     61          }
     62          return token;
     63      },
     64      async session({ session, token }) {
     65          if (session.user) {
     66              session.user.id = token.sub!;
     67              session.user.name = token.username as string;
     68
     69              const customToken = token as any;
     70              session.user.email = customToken.email;
     71              session.user.isAdmin = customToken.isAdmin;
     72          }
     73          return session;
     74      },
     75  },
     76} satisfies Omit<AuthConfig, "raw">;
     77}}}
     78
     79We use Auth.js for authentication and JWT for stateless session management. Passwords are never stored in plain text, instead bcrypt is used to hash and verify credentials. Input validation ensures that username and password are strings before any database query is made.
     80
     81{{{
     82export function getAuthState() {
     83    const c = ctx();
     84    const userId = parseSessionUserId(c.session?.user?.id);
     85    return {
     86        isLoggedIn: Boolean(userId),
     87        userId: userId ?? null,
     88        username: c.session?.user?.name ?? null,
     89        isAdmin: c.session?.user?.isAdmin,
     90        session: c.session,
     91    };
     92}
     93
     94export function requireUser() {
     95    const c = ctx();
     96    const userId = parseSessionUserId(c.session?.user?.id);
     97    if (!userId) throw Abort();
     98    return { c, userId };
     99}
     100
     101export async function requireAdmin() {
     102    const { c, userId } = requireUser();
     103
     104    if(!c.session?.user?.isAdmin) throw Abort();
     105
     106    const isAdmin = await drizzleQueries.isAdmin(c.db, userId);
     107    if (!isAdmin) throw Abort();
     108
     109    return { c, userId };
     110}
     111}}}
     112
     113We also implement strict role-based access control middleware. The `requireAdmin` function implements a Double-Check Strategy: it verifies the admin flag in the JWT session and performs a real-time database check. This ensures that if an admin's rights are revoked, they lose access immediately, even if their session is still active.