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;