lucia-auth / lucia

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

[Bug]: `email_verified` is always `undefined` in lucia even if its `1` in my sqlite database #1385

Closed deadcoder0904 closed 7 months ago

deadcoder0904 commented 7 months ago

Package

lucia

Describe the bug

clone this repo & switch to sqlite branch to see the error -> https://github.com/deadcoder0904/next-13-lucia-auth-drizzle-turso-sqlite-magic-link/tree/sqlite

i have tried lucia v3 in 2 different projects. the above one using email otp code & another is a private project using email verification links.

i always get undefined on emailVerified if i console.log something. this is from my private project:

{
  attributes: {
    email: 'a@a.com',
    emailVerified: 1,
   .
   .
   .
    refund: 0,
    isAdmin: 1
  }
}
{
  result: {
    user: {
      emailVerified: false,
      email: 'a@a.com',
      id: '01HNFDTVNC8KDHHTHH0W43XERF'
    },
    session: {
      id: 'qljwvah2ju4wn7q2cglmij1tswhsk2l8qfrsl5yt',
      userId: '01HNFDTVNC8KDHHTHH0W43XERF',
      fresh: false,
      expiresAt: 2024-01-31T09:37:15.000Z
    }
  }
}
{
  user: {
    emailVerified: false,
    email: 'a@a.com',
    id: '01HNFDTVNC8KDHHTHH0W43XERF'
  }
}

As you can see, the first time it shows emailVerified: 1 which is logged in attributes property inside new Lucia constructor:

export const lucia = new Lucia(adapter, {
    sessionCookie: {
        name: "user_session",
        expires: false,
        attributes: {
            secure: !IS_DEV,
        },
    },
    sessionExpiresIn: new TimeSpan(1, "m"), // 1 month
    getUserAttributes: (attributes) => {
        console.log({ attributes })
        return {
            emailVerified: Boolean(attributes.email_verified),
            email: attributes.email,
        }
    },
})

I'm logging result in validateRequest function like so:

const uncachedValidateRequest = async (): Promise<
    { user: User; session: Session } | { user: null; session: null }
> => {
    const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null

    if (!sessionId) {
        return {
            user: null,
            session: null,
        }
    }

    const result = await lucia.validateSession(sessionId)
    console.log({ result })
    // next.js throws when you attempt to set cookie when rendering page
    try {
        if (result.session && result.session.fresh) {
            const sessionCookie = lucia.createSessionCookie(result.session.id)
            cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes)
        }
        if (!result.session) {
            const sessionCookie = lucia.createBlankSessionCookie()
            cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes)
        }
    } catch {}
    return result
}

export const validateRequest = React.cache(uncachedValidateRequest)

And my dashboard/page.tsx has the final user logged:

const Dashboard = async () => {
    const { user } = await validateRequest()
    console.log({ user })
    if (!(user && user.emailVerified)) return redirect("/login")
    .
    .
    .
}

Both projects are quite similar so you can test out. I think its a bug in lucia itself. I'm unable to make email OTP or email links work in v3.

in v2, email links worked fine with copy-paste from docs. i didn't try email otp in v2 though so can't say about that.

LinusOP commented 7 months ago

In the code provided this looks to be a mismatch between the column name in the DB and what you're trying to access in getUserAttributes. The column name and raw data from the DB (attributes) has it called emailVerified. But in getUserAttributes you're trying to access attributes.email_verified. Hence why it is always undefined (that doesn't exist on attributes).

Try to access attributes.emailVerified instead and it should work.

Put this in discord too but thought it best to comment here as well.

pilcrowOnPaper commented 7 months ago

You'd have to be more specific for me to debug the issue. I need something reproducible. A minimal reproduction will help a lot here. Have you also checked if it's not an issue with Drizzle?

deadcoder0904 commented 7 months ago

@pilcrowOnPaper the sqlite branch is the minimal reproduction itself. it is as small as possible. ik it has other functionalities like signup, login but its optional as you can do db:seed to insert user data & then try login & verify email to see it doesn't work.

yes, i've checked straight for 2 days. i only use 2 packages: lucia & @lucia-auth/adapter-drizzle that exposes better-sqlite3 adapter.

i've also checked lucia packages & the code looks alright but it doesn't work on 2 separate repos of mine. i'd appreciate an example on email otp or email verification links in any framework. v2 worked but v3 isn't. i tried finding the bug but got nothing yet.

i'm 99% sure its a bug in lucia. i get undefined. at least, 0 could've been understandable. the above false only came because i wrapped it in Boolean and Boolean(undefined) is still false. please take a look.

deadcoder0904 commented 7 months ago

@LinusOP sorry i didn't see your comment above but its type-safe.

here's my schema:

export const userTable = sqliteTable("user", {
    id: text("id", { length: 255 }).primaryKey(),
    email: text("email", { length: 255 }).notNull().unique(),
    emailVerified: integer("email_verified").notNull(),

but i'll give it a shot. this might be a dumb mistake on my part.

pilcrowOnPaper commented 7 months ago

See https://github.com/deadcoder0904/next-13-lucia-auth-drizzle-turso-sqlite-magic-link/issues/2

deadcoder0904 commented 7 months ago

@LinusOP looks like you were right. i copy-pasted it straight as is from the lucia docs. and since it was type-safe, i didn't double-check.

i'm getting client-component error now but that worked i think:

{
  result: {
    user: {
      emailVerified: 1,
      email: 'a@a.com',
      id: '01HNFKFVFC2AP11Z6YGXDWJ08B'
    },
    session: {
      id: 'mw2093gumie0fim4fo3gja3lyf15riagcyjw3bgd',
      userId: '01HNFKFVFC2AP11Z6YGXDWJ08B',
      fresh: false,
      expiresAt: 2024-01-31T11:36:10.000Z
    }
  }
}
{
  user: {
    emailVerified: 1,
    email: 'a@a.com',
    id: '01HNFKFVFC2AP11Z6YGXDWJ08B'
  }
}

@pilcrowOnPaper i did find a couple of places for docs improvement. will probably send a pr now.

LinusOP commented 7 months ago

[...] and since it was type-safe, i didn't double-check.

Attributes is only typesafe based on your declared DatabaseUserAttributes interface, it does not actually ensure that interface matches with your DB schema. And in this case it didn't, DB schema iwas emailVerified but interface (and thus getUserAttributes) used email_verified.

So it's important to ensure that the interface matches the returned columns from the DB and your DB schema to get actual type safety.

deadcoder0904 commented 7 months ago

yeah, i fucked up. i think docs also used email_verified so i copy-pasted. see https://lucia-auth.com/guides/email-and-password/email-verification-codes

LinusOP commented 7 months ago

yeah, i fucked up. i think docs also used email_verified so i copy-pasted. see https://lucia-auth.com/guides/email-and-password/email-verification-codes

Easy mistake to make at the end of the day, most important is that it's resolved 😄