lucia-auth / lucia

Authentication, simple and clean
https://lucia-auth.com
MIT License
8.95k stars 460 forks source link

[Bug]: validateSession returns null. Sveltekit + MongoDB + Lucia #1647

Open EGSP opened 1 month ago

EGSP commented 1 month ago

Package

lucia

Describe the bug

I have a cookie that stores a record of authentication. In the hook function I read the cookie and check the validity of the session using the lucia.validateSession(session_id) function. The session id written in the cookie matches the id in the Mongo database. The problem I think is that Lucia cannot find the user during validation.

My user has an identifier of type ObjectId and is named “_id”. From what I have found on the internet and neighboring issues - I assume that lucia is trying to find a user with the property “_id” whose value is equal to the string value “user_id”. And apparently the types don't match

EGSP commented 1 month ago

I have tried to specify _id type, but it does not help

declare module "lucia" {
    interface Register {
        Lucia: typeof lucia;
        UserID: ObjectId;
    }
}

interface UserDocumentScheme {
    _id: string;
}

interface SessionDocumentScheme {
    _id: string;
    user_id: string;
    expires_at: Date;
}

export function prepare_lucia() {
    const _db = get_db();

    const users = _db.collection("users") as Collection<UserDocumentScheme>;
    const sessions = _db.collection("sessions") as Collection<SessionDocumentScheme>;

    adapter = new MongodbAdapter(sessions, users);

    lucia = new Lucia(adapter,
        {
            sessionCookie: {
                attributes: {
                    secure: false
                }
            },
            sessionExpiresIn: new TimeSpan(1,"w")
        }
    );
}
oscarhermoso commented 1 month ago

Does something like this help, @EGSP ?

  declare module "lucia" {
      interface Register {
          Lucia: typeof lucia;
           UserID: ObjectId;
+          DatabaseUserAttributes: UserDocumentScheme;
      }
  }

interface UserDocumentScheme {
    _id: string;
+   name: string;  
}

// ...

    lucia = new Lucia(adapter,
        {
+         getUserAttributes: (attributes) => {
+            return {
+               name: attributes.name
+             };
+     },
          sessionCookie: {
              attributes: {
                  secure: false
              }
          },
          sessionExpiresIn: new TimeSpan(1,"w")
        }
    );

I do not use MongoDB, but assume this work based on other open source projects that use "@lucia-auth/adapter-mongodb" on GitHub:

https://github.com/PriMacula/ecommerceTemplate/blob/e0ef494bc5d076259bb6a701aa8faea5c4740652/src/app/models/session.js#L17C14-L20

https://github.com/PriMacula/ecommerceTemplate/blob/e0ef494bc5d076259bb6a701aa8faea5c4740652/src/lib/auth.js#L7-L18

boylett commented 3 weeks ago

I was able to get it working by coercing the session ID to an ObjectId during validation:

import { ObjectId } from "bson";

// Validate the session
const { session, user } = await lucia.validateSession(
  // @ts-expect-error
  new ObjectId(sessionId)
);
C-Vane commented 2 weeks ago

I opened a PR for this could this solve the issue? https://github.com/lucia-auth/lucia/pull/1670

Kingnoba commented 2 weeks ago

I wrote a custom adapter extending MongodbAdapter to address the issue of using ObjectId for user lookups. It overrides the getSessionAndUser method to use ObjectId in the user lookup stage and then utilizes the $addFields stage to convert the _id back to a string for compatibility with Lucia.

I don't understand why the OG adapter doesn't do this in the first place if it insists on using strings elsewhere? I don't want to compromise indexing of users due to string id's.

xSenny commented 2 weeks ago

I was able to get it working by coercing the session ID to an ObjectId during validation:

import { ObjectId } from "bson";

// Validate the session
const { session, user } = await lucia.validateSession(
  // @ts-expect-error
  new ObjectId(sessionId)
);

Error: input must be a 24 character hex string, 12 byte Uint8Array, or an integer. What about this error? because the actual sessionId is 40 characters long

NRProjects commented 2 weeks ago

I have this working for me, no coercion needed.

const user = db.collection("users") as Collection<UserDoc>;
const session = db.collection("sessions") as Collection<SessionDoc>;

const adapter = new MongodbAdapter(session, user)

export const lucia = new Lucia(adapter, {
    sessionCookie: {
        name: "session",
        attributes: {
            secure: !dev
        }
    },

    getUserAttributes: (attributes) => {
        return {
            email: attributes.email,
        }
    },
});

declare module "lucia" {
    interface Register {
        Lucia: typeof lucia;
        DatabaseUserAttributes: User;
        UserId: ObjectId
    }
}

interface UserDoc {
    _id: ObjectId;
}

const session = await lucia.createSession(existingUser._id, {});
const sessionCookie = await lucia.createSessionCookie(session.id);
event.cookies.set(sessionCookie.name, sessionCookie.value, {
    path: '.',
    ...sessionCookie.attributes
});

const { session, user } = await lucia.validateSession(sessionId);

I'm still learning this whole user auth thing so I could be doing something fundamentally wrong, but I've been banging my head against a wall for hours now so some delirium is expected

Kingnoba commented 2 weeks ago
declare module "lucia" {
    interface Register {
        Lucia: typeof lucia;
        DatabaseUserAttributes: User;
        UserId: ObjectId
    }
}

I mean that's in the docs, but I'm one of those crazy untalented devs who don't like killing themselves with TypeScript, so instead made a custom adapter.