lucia: [Bug]: date.getTime() is not a function (originating in oslo) -> but the core problem is that nodejs-adapter from Lucia doesn't convert expiresAt as a date

Package

​@lucia-auth/adapter-postgresql

Describe the bug

So my first time deep delving into the codebase of Lucia so bear with me:

  1. I first verified that expires_at is timestamp in the database with timezone.
  2. When Lucia tries to validate the session it uses this.adapter.getSessionAndUser(sessionId); -> which will in turn go the database, fetch the session, convert the fields_with_underscores to fieldsWithUnderscores (ie. expires_at -> expiresAt).
  3. Next it will use oslo to check if the session isWithinExpirationDate -> but the expiresAt has been loaded as a string even though it is defined as a timestamp with zone.

I am using the following adapter for Postgres

const adapter = new NodePostgresAdapter(pool, {
	user: 'auth_user',
	session: 'session'
});

Here is how the database session looks like when loaded from the db by lucia.

{
  userId: 'scp1x8lzy270qh5',
  id: 'rqcui4nih4o6lix32xmxp1zzbukr6jp5qozm7m0q',
  expiresAt: '2024-03-06 19:29:17.726+00',
  attributes: {}
}

Here is the stack trace where it fails ultimately:

TypeError: date.getTime is not a function
    at isWithinExpirationDate (file:///E:/Projects/my-project/node_modules/.pnpm/oslo@1.0.1/node_modules/oslo/dist/index.js:34:30)

This is a severe issue I can’t circumvent since upgrading (still in progress obviously) to v3. Any insight into this issue is more than welcomed. 🙏

About this issue

  • Original URL
  • State: closed
  • Created 5 months ago
  • Reactions: 2
  • Comments: 18 (16 by maintainers)

Most upvoted comments

For me, this is the solution: change the timestamp options.mode from “string” to “date”.

From this:

expiresAt: timestamp("expires_at", {
    withTimezone: true,
    mode: "string",
  }).notNull(),

To this:

expiresAt: timestamp("expires_at", {
    withTimezone: true,
    mode: "date",
  }).notNull(),

So the session table schema will look something like this:

export const sessionsTable = pgTable("sessions", {
  id: text("id").primaryKey().notNull(),
  userId: text("user_id")
    .notNull()
    .references(() => usersTable.id),
  expiresAt: timestamp("expires_at", {
    withTimezone: true,
    mode: "date",
  }).notNull(),
});

Literally just hit this one as well 😅 Thanks @callmeberzerker Regarding https://github.com/lucia-auth/lucia/issues/1424#issuecomment-1947238448 I believe this is Drizzle intended behaviour (see https://github.com/drizzle-team/drizzle-orm/pull/1659)

@pilcrowOnPaper it might be worth fixing this at the adapter level or at the oslo level (accept string) as this could happen with other ORMs, not only Drizzle.

Something like this would work : date instanceof new Date() ? new Date(date) : date (I’m using this in a pnpm patch)

I’ve encountered this as well and thank you for your contribution to the docs!

Oh wow, that’s sneaky. Yeah it should be probably mentioned in the docs - I guess at the top of the PostgreSQL database page.

After a lot of digging I found the source of my issue -> and I think the docs should warn of this.

If you use drizzle ORM in your project you must use the DrizzlePostgreSQLAdapter as well, since the drizzle PostgreSQL driver will override the default handling(parsers) for several database types.

See source code for drizzle-orm here:

https://github.com/drizzle-team/drizzle-orm/blob/0da1cba84da08bc0407821c9ab55b3e780ff5e3f/drizzle-orm/src/node-postgres/driver.ts#L41

Mystery solved.

@pilcrowOnPaper shout if you agree on the docs and if you would accept a PR

Just a small update, I managed to checkout the Lucia repo -> and I can verify that the test works. Trying to find now what is the discrepancy between my project and the setup - or potentially some other function is being used in the production code that is not used by the test -> that forces the expiresAt to be marshalled as string.

It’s Valentines Day so I guess I have nothing better to do. 🤣

Can you share your Drizzle/db schema? There’s also a new Drizzle adapter https://lucia-auth.com/database/drizzle