Ash Gharibyan
Ash Gharibyan

Reputation: 141

Next js next auth credentials with custom user

I am building a project using the t3 stack Using app router, TRPC, prisma, and next auth.

With the initial set up and even on documentation, the user model for prisma they give is with an ID, but I need my user model to only have username email password, with username being the primary key.

When I change the prisma Model and make the migrations, And in the configurations for next auth I’m using the credentials provider, And inside of it in the authorize function, it gives me an error because it expects the user To have an ID.

I cannot find anywhere in the documentation or anywhere else. How can I make my authorization work with just username email and password and without an ID.

Anyone have any suggestions?

here is my code:

this is - src/server/auth.ts

/**
 * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
 * object and keep type safety.
 *
 * @see https://next-auth.js.org/getting-started/typescript#module-augmentation
 */

declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {
      username: string;
      email: string;
      first_name: string;
      last_name: string;
    } & DefaultSession["user"];
  }

  interface User {
    username: string;
    email: string;
    password: string;
    first_name: string;
    last_name: string;
  }
}

/**
 * Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
 *
 * @see https://next-auth.js.org/configuration/options
 */
export const authOptions: NextAuthOptions = {
  callbacks: {
    session: ({ session, user }) => ({
      ...session,
      user: {
        ...session.user,
        username: user.username,
        email: user.email,
        first_name: user.first_name,
        last_name: user.last_name,
      },
    }),
  },
  // adapter: PrismaAdapter(db) as Adapter,
  providers: [
    CredentialsProvider({
      // The name to display on the sign in form (e.g. 'Sign in with...')
      name: "Credentials",
      // The credentials is used to generate a suitable form on the sign in page.
      // You can specify whatever fields you are expecting to be submitted.
      // e.g. domain, username, password, 2FA token, etc.
      // You can pass any HTML attribute to the <input> tag through the object.
      credentials: {
        username: { label: "Username", type: "text", placeholder: "jsmith" },
        password: { label: "Password", type: "password" },
      },

      async authorize(credentials, req) {
        const creds = await z
          .object({
            username: z.string(),
            password: z.string().min(6),
          })
          .parseAsync(credentials);

        // Find user by username
        const user = await db.user.findUnique({
          where: { username: creds.username },
        });

        // If user not found, return null
        if (!user) return null;

        // Verify password
        const valid = await verifyPassword(creds.password, user.password);

        // If password is invalid, return null
        if (!valid) return null;

        // If user is found and password is valid, return user
        return user;
      },
    }),
    /**
     * ...add more providers here.
     *
     * Most other providers require a bit more work than the Discord provider. For example, the
     * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account
     * model. Refer to the NextAuth.js docs for the provider you want to use. Example:
     *
     * @see https://next-auth.js.org/providers/github
     */
  ],
};

this is my prisma schema

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
    provider = "prisma-client-js"
}

generator zod {
  provider = "zod-prisma-types"
}

datasource db {
    provider = "postgresql"
    // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below
    // Further reading:
    // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema
    // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string
    url      = env("DATABASE_URL")
}



model Session {
    id           String   @id @default(cuid())
    sessionToken String   @unique
    usernameId       String
    expires      DateTime
    user         User     @relation(fields: [usernameId], references: [username], onDelete: Cascade)
}

model User {
  username String @unique
  email String @unique
  password String 
  first_name String
  last_name String
    sessions      Session[]
}

model VerificationToken {
    identifier String
    token      String   @unique
    expires    DateTime

    @@unique([identifier, token])
}

this is the error

Type '(credentials: Record<"username" | "password", string> | undefined, req: Pick<RequestInternal, "body" | "query" | "headers" | "method">) => Promise<...>' is not assignable to type '(credentials: Record<"username" | "password", string> | undefined, req: Pick<RequestInternal, "body" | "query" | "headers" | "method">) => Awaitable<...>'.
  Type 'Promise<{ username: string; email: string; password: string; first_name: string; last_name: string; } | null>' is not assignable to type 'Awaitable<User | null>'.
    Type 'Promise<{ username: string; email: string; password: string; first_name: string; last_name: string; } | null>' is not assignable to type 'PromiseLike<User | null>'.
      Types of property 'then' are incompatible.
        Type '<TResult1 = { username: string; email: string; password: string; first_name: string; last_name: string; } | null, TResult2 = never>(onfulfilled?: ((value: { username: string; email: string; password: string; first_name: string; last_name: string; } | null) => TResult1 | PromiseLike<...>) | null | undefined, onreject...' is not assignable to type '<TResult1 = User | null, TResult2 = never>(onfulfilled?: ((value: User | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => PromiseLike<...>'.
          Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
            Types of parameters 'value' and 'value' are incompatible.
              Type '{ username: string; email: string; password: string; first_name: string; last_name: string; } | null' is not assignable to type 'User | null'.
                Property 'id' is missing in type '{ username: string; email: string; password: string; first_name: string; last_name: string; }' but required in type 'User'.ts(2322)
types.d.ts(504, 5): 'id' is declared here.
credentials.d.ts(13, 5): The expected type comes from property 'authorize' which is declared here on type 'UserCredentialsConfig<{ username: { label: string; type: string; placeholder: string; }; password: { label: string; type: string; }; }>'
(property) authorize: (credentials: Record<"username" | "password", string> | undefined, req: Pick<RequestInternal, "body" | "query" | "headers" | "method">) => Awaitable<User | null>

Upvotes: 0

Views: 501

Answers (1)

Yilmaz
Yilmaz

Reputation: 49561

Can yu use @id in User

model User {
  // @id makes it primary key. 
  username   String  @id @unique
}

Upvotes: 0

Related Questions