import { TinaNodeBackend, LocalBackendAuthProvider, } from "@tinacms/datalayer"; import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { getServerSession } from "next-auth/next"; import databaseClient from "../../../tina/__generated__/databaseClient"; const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === "true"; const TINA_CREDENTIALS_PROVIDER_NAME = "TinaCredentials"; const authenticate = async ( dbClient: typeof databaseClient, username: string, password: string ) => { try { const result = await dbClient.authenticate({ username, password }); return result.data?.authenticate || null; } catch (e) { return null; } }; const authOptions = { callbacks: { jwt: async ({ token: jwt, account }: any) => { if (account) { try { if (jwt?.sub) { const result = await databaseClient.authorize({ sub: jwt.sub }); jwt.role = result.data?.authorize ? "user" : "guest"; jwt.passwordChangeRequired = result.data?.authorize?._password?.passwordChangeRequired || false; } } catch { // ignore auth errors } if (jwt.role === undefined) { jwt.role = "guest"; } } return jwt; }, session: async ({ session, token: jwt }: any) => { session.user.role = jwt.role; session.user.passwordChangeRequired = jwt.passwordChangeRequired; session.user.sub = jwt.sub; return session; }, }, session: { strategy: "jwt" as const }, secret: process.env.NEXTAUTH_SECRET as string, providers: [ CredentialsProvider({ name: TINA_CREDENTIALS_PROVIDER_NAME, credentials: { username: { label: "Username", type: "text" }, password: { label: "Password", type: "password" }, }, authorize: async (credentials: any) => authenticate(databaseClient, credentials.username, credentials.password), }), ], }; const handler = TinaNodeBackend({ authProvider: isLocal ? LocalBackendAuthProvider() : { initialize: async () => {}, isAuthorized: async (req: any, res: any) => { const session = await getServerSession(req, res, authOptions); if (!req.session) { Object.defineProperty(req, "session", { value: session, writable: false, }); } if (!(session as any)?.user) { return { errorCode: 401, errorMessage: "Unauthorized", isAuthorized: false, }; } if ((session as any)?.user?.role !== "user") { return { errorCode: 403, errorMessage: "Forbidden", isAuthorized: false, }; } return { isAuthorized: true }; }, extraRoutes: { auth: { secure: false, handler: async (req: any, res: any, opts: any) => { const url = new URL( req.url, `http://${req.headers?.host || "localhost"}` ); const authSubRoutes = url.pathname ?.replace(`${opts.basePath}auth/`, "") ?.split("/"); req.query.nextauth = authSubRoutes; await NextAuth(authOptions)(req, res); }, }, }, }, databaseClient, }); export default (req: any, res: any) => { return handler(req, res); }; export { authOptions };