nuxt-auth icon indicating copy to clipboard operation
nuxt-auth copied to clipboard

TypeScript: Can't extend callback params type

Open kei-ichi opened this issue 1 year ago • 0 comments

Environment


  • Operating System: Darwin
  • Node Version: v22.12.0
  • Nuxt Version: 3.15.2
  • CLI Version: 3.20.0
  • Nitro Version: 2.10.4
  • Package Manager: [email protected]
  • Builder: -
  • User Config: compatibilityDate, ssr, devtools, modules, runtimeConfig, auth
  • Runtime Modules: @sidebase/[email protected]
  • Build Modules: -

Reproduction

You don't need a repo to "reproduce" this issue, just add the @side-base/nuxt-auth model with "auth.js" as it provider then try to extend the callback parameters type using "declare module".

Just in case below is my "minimal" config in the completely new Nuxt 3 project.

nuxt.config.ts file

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  ssr: false,

  devtools: { enabled: true },

  modules: ['@sidebase/nuxt-auth'],

  runtimeConfig: {
    authOrigin: '',
  },

  auth: {
    isEnabled: true,
    originEnvKey: 'NUXT_AUTH_ORIGIN',
    baseURL: '/api/v1/auth',
    provider: {
      type: 'authjs',
      trustHost: true,
      defaultProvider: 'credentials',
      addDefaultCallbackUrl: true,
    },
    globalAppMiddleware: true,
  },
});

NuxtAuthHandler files, you can ignore almost all of it, just focus on the callback code:

import CredentialsProvider from 'next-auth/providers/credentials';
import { NuxtAuthHandler } from '#auth';

interface CustomUser {
  id: string;
  username: string;
  password: string;
}

export default NuxtAuthHandler({
  // A secret string you define, to ensure correct encryption
  secret: 'C4A01200-A1C5-47F5-A392-3F693B973489',
  providers: [
    // @ts-expect-error Use .default here for it to work during SSR.
    CredentialsProvider.default({
      id: 'credentials',
      name: 'Credentials',
      credentials: {
        username: { label: 'Username', type: 'text', placeholder: 'Username' },
        password: {
          label: 'Password',
          type: 'password',
          placeholder: 'Password',
        },
      },
      async authorize(credentials: { username: string; password: string }) {
        // Add logic here to look up the user from the credentials supplied

        console.log(`=======================================`);
        console.log(`Authorized credentials:`);
        console.log(JSON.stringify(credentials));
        console.log(`=======================================`);

        const user = {
          id: crypto.randomUUID(),
          username: credentials.username,
          // password: credentials.password,
        };

        console.log(`Credentials: ${JSON.stringify(user)}`);

        if (credentials.username && credentials.password) {
          // Any object returned will be saved in `user` property of the JWT
          return user;
        } else {
          // If you return null then an error will be displayed advising the user to check their details.
          return null;

          // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
        }
      },
    }),
  ],
  // Callbacks should be here at the root level
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      console.log(`------------------ BEFORE SIGN IN -------------------`);
      console.log(user);
      console.log(`------------------ BEFORE SIGN IN -------------------`);
      return true;
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      console.log(`------------------JWT-------------------`);
      console.log('JWT Callback - Input token:', token);
      console.log('JWT Callback - Input user:', user);
      console.log(`------------------JWT-------------------`);

      if (user) {
        return {
          ...token,
          userData: {
            // Store user data in a dedicated property
            id: user.id,
            username: user.username,
          },
        };
      }

      return token;
    },
    async session({ session, user, token }) {
      console.log('Session Callback - Input session:', session);
      console.log('Session Callback - Input token:', token);

      // Make sure user data is copied to session
      if (token.userData) {
        session.user = token.userData;
      }

      console.log('Session Callback - Returning session:', session);
      return session;
    },
    async redirect({ url, baseUrl }) {
      console.log(`------------------ REDIRECT URL -------------------`);
      console.log(url);
      console.log(baseUrl);
      console.log(`---------------------------------------------------`);
      return url;
    },
  },
});

This part for more specific

      if (user) {
        return {
          ...token,
          userData: {
            // Store user data in a dedicated property
            id: user.id,
            username: user.username, <---------------- THIS IS WHERE THE ISSUE HAPPEN
          },
        };
      }

The user.username got the below error:

Vue: Property username does not exist on type User | AdapterUser
Property username does not exist on type User

But I did extend that User interface/types like this:

import '#auth';

declare module '#auth' {
  interface User {
    id: string;
    username: string;
    password?: string;
  }
}

And if I click to the User type when hover my mouse to the user I got property type as below:

export interface module:.auth. User {    
       id: string     
       username: string 
}
 
shared/types/nuxt-auth.ts

Image

Image

Image

Image

Describe the bug

TypeScript: Cannot extend User type despite it being declared and visible in IDE and the official docs don't have any "section" on when using with TypeScript but the original package Auth.js have

I did describe this issue detailed in the Reproduction sections so I will not repeat it here.

I will be very appreciated, If anyone have same issue or have a solutions to this issue, please add a comment.

To this package dev teams, please add a document about TypeScript usage.

Additional context

No response

Logs


kei-ichi avatar Jan 20 '25 13:01 kei-ichi