import type { NextAuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { cookies } from "next/headers"; import { getAppcondaClient } from "../getAppcondaClient"; import { Account } from "../modules/account/service"; import { getEnv } from "../lib/env"; import { getSDKForCurrentUser } from "../getSDKForCurrentUser"; import { Query } from "../query"; /* const getEnv = () => { return { APPCONDA_ENDPOINT: 'process.env.APPCONDA_ENDPOINT', APPCONDA_CLIENT_ENDPOINT: 'process.env.APPCONDA_CLIENT_ENDPOINT', _SERVICE_TOKEN: 'process.env._SERVICE_TOKEN', ENTERPRISE_LICENSE_KEY: 'process.env.ENTERPRISE_LICENSE_KEY', }; }; */ export async function signIn({ userName, password }: { userName: string, password: string }) { const adminClient = await getAppcondaClient(); const account = new Account(adminClient); const session = await account.createEmailPasswordSession(userName, password); const c = await cookies(); c.set('a_session', session.secret, { path: "/", httpOnly: true, sameSite: "strict", secure: true, }); return session; } export const authOptions = (() => { let options : any= null; return () => { if (options == null) { options = { providers: [ CredentialsProvider({ id: "credentials", // The name to display on the sign in form (e.g. "Sign in with...") name: "Credentials", // The credentials is used to generate a suitable form on the sign in page. // You can specify whatever fields you are expecting to be submitted. // e.g. domain, username, password, 2FA token, etc. // You can pass any HTML attribute to the tag through the object. credentials: { email: { label: "Email Address", type: "email", placeholder: "Your email address", }, password: { label: "Password", type: "password", placeholder: "Your password", }, totpCode: { label: "Two-factor Code", type: "input", placeholder: "Code from authenticator app" }, backupCode: { label: "Backup Code", type: "input", placeholder: "Two-factor backup code" }, }, async authorize(credentials, _req) { debugger; let user; const appcondaSession = await signIn({ userName: credentials?.email as string, password: credentials?.password as string }); console.log(credentials); /* try { user = await prisma.user.findUnique({ where: { email: credentials?.email, }, }); } catch (e) { console.error(e); throw Error("Internal server error. Please try again later"); } if (!user || !credentials) { throw new Error("Invalid credentials"); } if (!user.password) { throw new Error("Invalid credentials"); } const isValid = await verifyPassword(credentials.password, user.password); if (!isValid) { throw new Error("Invalid credentials"); } if (user.twoFactorEnabled && credentials.backupCode) { if (!ENCRYPTION_KEY) { console.error("Missing encryption key; cannot proceed with backup code login."); throw new Error("Internal Server Error"); } if (!user.backupCodes) throw new Error("No backup codes found"); const backupCodes = JSON.parse(symmetricDecrypt(user.backupCodes, ENCRYPTION_KEY)); // check if user-supplied code matches one const index = backupCodes.indexOf(credentials.backupCode.replaceAll("-", "")); if (index === -1) throw new Error("Invalid backup code"); // delete verified backup code and re-encrypt remaining backupCodes[index] = null; await prisma.user.update({ where: { id: user.id, }, data: { backupCodes: symmetricEncrypt(JSON.stringify(backupCodes), ENCRYPTION_KEY), }, }); } else if (user.twoFactorEnabled) { if (!credentials.totpCode) { throw new Error("second factor required"); } if (!user.twoFactorSecret) { throw new Error("Internal Server Error"); } if (!ENCRYPTION_KEY) { throw new Error("Internal Server Error"); } const secret = symmetricDecrypt(user.twoFactorSecret, ENCRYPTION_KEY); if (secret.length !== 32) { throw new Error("Internal Server Error"); } const isValidToken = (await import("./totp")).totpAuthenticatorCheck(credentials.totpCode, secret); if (!isValidToken) { throw new Error("Invalid second factor code"); } } */ console.log("asafdf") return { id: appcondaSession.userId, email: appcondaSession.providerUid, emailVerified: true, imageUrl: "", }; }, }), CredentialsProvider({ id: "token", // The name to display on the sign in form (e.g. "Sign in with...") name: "Token", // The credentials is used to generate a suitable form on the sign in page. // You can specify whatever fields you are expecting to be submitted. // e.g. domain, username, password, 2FA token, etc. // You can pass any HTML attribute to the tag through the object. credentials: { token: { label: "Verification Token", type: "string", }, }, async authorize(credentials, _req) { let user; /* try { if (!credentials?.token) { throw new Error("Token not found"); } const { id } = await verifyToken(credentials?.token); user = await prisma.user.findUnique({ where: { id: id, }, }); } catch (e) { console.error(e); throw new Error("Either a user does not match the provided token or the token is invalid"); } if (!user) { throw new Error("Either a user does not match the provided token or the token is invalid"); } if (user.emailVerified) { throw new Error("Email already verified"); } user = await updateUser(user.id, { emailVerified: new Date() }); */ return user || null; }, }), // Conditionally add enterprise SSO providers ...(getEnv().ENTERPRISE_LICENSE_KEY ? [] : []), ], callbacks: { async jwt({ token }) { const { users } = await getSDKForCurrentUser(); const userList = await users.list([Query.equal("email", token.email!)]) const user = userList.users[0] ?? {}; /* const existingUser = await getUserByEmail(token?.email!); if (!existingUser) { return token; } */ return { ...token, //@ts-ignore profile: { id: user.$id, ...user }, }; }, async session({ session, token }) { //@ts-ignore session.user.id = token?.id; //@ts-ignore session.user = token.profile; return session; }, //@ts-ignore async signIn({ user, account }: { user: any; account: Account | null }) { /* if (account?.provider === "credentials" || account?.provider === "token") { // check if user's email is verified or not if (!user.emailVerified && !EMAIL_VERIFICATION_DISABLED) { throw new Error("Email Verification is Pending"); } return true; } if (ENTERPRISE_LICENSE_KEY) { return handleSSOCallback({ user, account }); } */ return true; }, }, pages: { signIn: "/auth/login", signOut: "/auth/logout", error: "/auth/login", // Error code passed in query string as ?error= }, } } return options; } })();