UNPKG

create-codehuddle-nextjs

Version:

🚀 Interactive CLI tool to generate modern Next.js 15 apps with authentication, payments, dashboard, PWA, and more. Choose your stack with TypeScript, Tailwind CSS, shadcn/ui, and enterprise features.

144 lines (129 loc) • 4.52 kB
import type { NextAuthConfig } from 'next-auth'; import type { JWT } from 'next-auth/jwt'; import NextAuth from 'next-auth'; import Credentials from 'next-auth/providers/credentials'; import GitHub from 'next-auth/providers/github'; import Google from 'next-auth/providers/google'; const API_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'https://honest-dog-dev-104c88de45c2.herokuapp.com/api'; export const authConfig: NextAuthConfig = { providers: [ GitHub({ clientId: process.env.AUTH_GITHUB_ID, clientSecret: process.env.AUTH_GITHUB_SECRET, }), Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET, }), Credentials({ name: 'Credentials', credentials: { email: { label: 'Email', type: 'email' }, password: { label: 'Password', type: 'password' }, }, async authorize(credentials) { try { const response = await fetch(`${API_URL}/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: credentials?.email, password: credentials?.password, }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || 'Authentication failed'); } return { id: data.user.userId.toString(), email: data.user.email, name: `${data.user.firstName} ${data.user.lastName}`, role: data.user.role, accessToken: data.tokens.access_token, refreshToken: data.tokens.refresh_token, accessTokenExpires: Date.now() + data.tokens.access_token_expires_in * 1000, }; } catch (error) { console.error('Credentials authentication error:', error); return null; } }, }), ], pages: { signIn: '/sign-in', }, callbacks: { async jwt({ token, user, account }) { if (account && user) { token.accessToken = account.provider === 'credentials' ? user.accessToken : account.access_token; token.refreshToken = user.refreshToken; token.accessTokenExpires = user.accessTokenExpires; token.user = { id: user.id ?? '', email: user.email ?? '', name: user.name ?? '', role: user.role || 'user', }; } if (token.accessTokenExpires && typeof token.accessTokenExpires === 'number' && Date.now() < token.accessTokenExpires) { return token; } return token.refreshToken ? await refreshAccessToken(token) : token; }, async session({ session, token }) { if (token.user) { session.user = { ...session.user, ...token.user, }; } token.accessTokenExpires = Date.now() + 15 * 24 * 60 * 60 * 1000; // 15 days ago if (token.accessTokenExpires && typeof token.accessTokenExpires === 'number' && Date.now() < token.accessTokenExpires) { session.accessToken = token.accessToken; session.error = undefined; return session; } else { const response = await refreshAccessToken(token); token.accessToken = response.accessToken; } session.accessToken = token.accessToken; session.error = token.error; return session; }, }, session: { strategy: 'jwt', maxAge: 15 * 24 * 60 * 60, // 15 days }, }; async function refreshAccessToken(token: JWT): Promise<JWT> { try { const response = await fetch(`${API_URL}/v1/auth/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token.refreshToken}`, }, }); const data = await response.json(); if (!response.ok) { throw data; } return { ...token, accessToken: data.access_token, accessTokenExpires: Date.now() + data.access_token_expires_in * 1000, refreshToken: data.refresh_token ?? token.refreshToken, error: undefined, }; } catch (error) { console.error('RefreshAccessTokenError:', error); return { ...token, error: 'RefreshAccessTokenError', }; } } export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);