lucia-auth / lucia

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

how can i define a one to one extra DatabaseUserAttributes #1576

Closed akram-ammour closed 3 months ago

akram-ammour commented 4 months ago

thanks in advance for answering me, and i would like to thank every single person that helped build this and make this easy to understand and to write. Concerning my issue for context i m using nextjs with prisma and i can't seem to find how to define extra attributes in my schema , so i have a user table that has attached to it a profile table that contains the username which i want to be able to retrieve in the attributes if the user has a username yet or not, but the problem is when i get the current user i don't seem to find that extra profile user attributes , here is my code :

export const lucia = new Lucia(adapter, {
  sessionCookie: {
    expires: false,
    attributes: {
      secure: process.env.NODE_ENV === "production",
    },
  },
  getUserAttributes: (attributes) => {
    return {
      // attributes has the type of DatabaseUserAttributes
      id: attributes.id,
      fname: attributes?.profile?.fname,
      lname: attributes?.profile?.lname,
      fullname:
        attributes?.profile?.fname &&
        attributes?.profile?.lname &&
        `${attributes.profile.fname} ${attributes.profile.lname}`,
      email: attributes.email,
      username: attributes?.profile?.username,
      role: attributes.role,
    };
  },
});

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

interface DatabaseUserAttributes {
  email: string;
  id: string;
  // check the profile not working
  profile?: {
    fname: string;
    lname: string;
    username: string;
  };
  role: Role;
}

here is my prisma db schema

model User {
  id       String @id @default(cuid())
  fname    String
  lname    String
  email    String @unique
  password String
  role     Role   @default(user)

  profile  Profile?
  sessions Session[]
}

model Profile {
  id String @id @default(cuid())

  user   User   @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId String @unique

  image      String?
  username   String?  
  phoneNumber     String
....
}

i would be so grateful if i could be provided with a solution and

mkreuzmayr commented 4 months ago

Hey @akram-ammour, as profile is a reference to another table in prisma, lucia does not automatically include it in the user query. The two options you got would either be putting everything from your Profile table into the User table directly or manually querying for the Profile table in getUserAttributes.

gregorskii commented 4 months ago

I am having a similar issue in typescript. I have an additional property in my prisma schema, attached it to DatabaseSessionAttributes but when I create a session trypescript errors that the property is not being defined when the sessions is created. But the field is auto generated by the schema.

The field is "createdAt"

model Session {
  id        String   @id
  userId    String
  createdAt DateTime @default(now())
  expiresAt DateTime
  ip        String?
  user      User     @relation(references: [id], fields: [userId], onDelete: Cascade)
}
Screenshot 2024-05-24 at 9 41 45 PM
pilcrowOnPaper commented 4 months ago

@gregorskii Can you share your DatabaseSessionAttributes?

gregorskii commented 4 months ago

I tried a couple options:

--- more like docs:

import { User, Session } from '@prisma/client'

...

declare module 'lucia' {
  interface Register {
    Lucia: typeof lucia
    DatabaseUserAttributes: Omit<User, 'id'>
    DatabaseSessionAttributes: Session
}
}

The only one that will compile is basic and manual:

declare module 'lucia' {
  interface Register {
    Lucia: typeof lucia
    DatabaseUserAttributes: DatabaseUserAttributes
    DatabaseSessionAttributes: DatabaseSessionAttributes
  }
}

interface DatabaseSessionAttributes {
  ip: string
}

interface DatabaseUserAttributes {
  email: string
}

So I understand the mental model --- these properties are intended to let Lucia know what other fields are in the db so it can return them as well? Correct?

When I add fields to these it seems it changes the signature of the createSession method:

const session = await lucia.createSession(user.id, { ip })

Errors on that line if anything else is added to the session, I was originally trying to add "country" or "createdAt" so I can track when sessions were created without date math on the expires.

Not super important the session has anything extra, just trying to get how it works.

Thank you.

So far it's been a bit of a learning curve but the library is really good, thank you!

ChoaibMouhrach commented 3 months ago

I'm having the same issue. I wish the library exposed a function to manually query the user and any extra attributes from relations. In my case, I have a role that I want to add to the user for verification purposes.

export const roles = sqliteTable("roles", {
  id: id(),
  name: text("name").notNull().unique(),
});

export const users = sqliteTable("users", {
  id: text("id").notNull().primaryKey(),
  email: text("email").notNull(),
  roleId: text("roleId")
    .notNull()
    .references(() => roles.id, {
      onDelete: "cascade",
    }),
});
pilcrowOnPaper commented 3 months ago

The official adapters are intended to be good enough for 90-95% of users so I'd just recommend creating custom adapters if you need more functionality