authConfig for Nextjs + Strapi with Google

./src/app/lib/authConfig.ts
import type { NextAuthOptions, DefaultSession } from 'next-auth';
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import { authService } from "@/app/services/authService"; // fetches for User data in a function
// import Facebook from "next-auth/providers/facebook";
 
 
declare module "next-auth" { // Declare as global to avoid typescript error
  interface Session {
    user: {
      jwt: string;
    } & DefaultSession["user"]
  }
  
  interface User {
    password?: string;
  }
}
 
 
export const authConfig: NextAuthOptions = {
  secret: process.env.NEXT_AUTH_SECRET, // As in nextjs and next-auth documentation
 
  providers: [
    GoogleProvider({ // Google login provider
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
    //TO BE ENABLED
    // Facebook({
    //   clientId: process.env.AUTH_FACEBOOK_ID as string,
    //   clientSecret: process.env.AUTH_FACEBOOK_SECRET as string,
    // }),
    CredentialsProvider({ // Strapi as local provider
      // The name to display on the sign in form (e.g. "Sign in with...")
      name: "Email",
      // `credentials` is used to generate a form on the sign in page.
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      // You can pass any HTML attribute to the <input> tag through the object.
      credentials: {
        email: { label: "Email", type: "text", placeholder: "Email" },
        password: { label: "Senha", type: "password" }
      },
      async authorize(credentials, req) {
        if (credentials) {
          try {
            const response = await authService.signIn({ email: credentials.email, password: credentials.password }); // Normal fetch("api") organized with function
 
            if ("user" in response) { //Ensure user in response for type safe and possible fails in network and fetch
              const { jwt, user } = response;
        
              if (!user.email || !user.id) {
                throw new Error("Invalid user data:", user.email); // throw error to fix in development.
                return null;
              }
        
              // All good! Object was configured to get jwt and main informations at first level.
              return {
                jwt,
                id: `${user.id}`,
                email: user.email,
                name: "",
                password: credentials.password
              };
            }
 
            // If passed return user informations something went wrong, check console.        
            console.error("Invalid response from authService.authorize ", response);
            return null;
          } catch (error) {
            // Other unexpected erros
            console.log("Credentials authorize error", error);
            return null;
          }
        }
        
        // Credentials failed at provider level
        return null;
      }
    }),
  ],
 
  callbacks: {
    async signIn({ user, account }) {
      try {
        const { email } = user;
        
        // Basic check to passs
        if (!email) {
          console.error("Incomplete data: Email or Password missing.");
          return false;
        }
 
        // All good and login will pass
        if (account?.provider && user.email) {
          return true;
        }
 
        // Case using local Provider, go straight to sign in
        if(!("password" in user)){
          return false;
        } else {
          const password = user.password as string;
          const response = await authService.signIn({email, password: password});
  
          if("user" in response && response?.user){
            return true;
          }
        }
 
        return false;
      } catch (error) {
        console.error("SignIn Error:", error instanceof Error ? error.message : error);
        return false;
      }
    },
    
    async jwt({ token, user, account }) {
      // Case access_token will fill jwt token
      if (account?.access_token) {
        const response = await authService.authUpsert({ access_token: account.access_token, provider: account.provider });
        // Another fetch containing 
        // const url = `${api_url}/api/auth/${provider}/callback?access_token=${access_token}`;
        if ("jwt" in response) {
          account.access_token = response.jwt;
        }
      }
 
 
      return {
        ...token,
        ...user,
        ...account,
        jwt: account?.access_token || token.jwt,
      };
    },
 
    // Session object
    async session({ session, token,  }) {
      const { password, ...tokenNoPassowrd } = token;
 
      return {
        ...session,
        user: {
          jwt: token.access_token,
          ...tokenNoPassowrd,
        },
      }
    },
 
    // Redirect according to next-auth docs. Mine is not working properly yet.
    async redirect({ url, baseUrl }) {
      if (url.startsWith("/")) return `${baseUrl}${url}`;
 
      if (new URL(url).origin === baseUrl) return url;
      
      return baseUrl
    }
  },
 
  // Custom login page to replace next-auth default
  pages: {
    signIn: "/account/login-options",
  },
} satisfies NextAuthOptions;