diff --git a/apps/api/src/controllers/config.ts b/apps/api/src/controllers/config.ts index f62f773f7..c27556150 100644 --- a/apps/api/src/controllers/config.ts +++ b/apps/api/src/controllers/config.ts @@ -4,7 +4,7 @@ // SSO Provider // Portal Locale // Feature Flags - +import { GoogleAuth, OAuth2Client } from "google-auth-library"; import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import nodeMailer from "nodemailer"; @@ -249,6 +249,10 @@ export function configRoutes(fastify: FastifyInstance) { reply: replyto, username, password, + serviceType, + clientId, + clientSecret, + redirectUri, }: any = request.body; const email = await prisma.email.findFirst(); @@ -262,6 +266,8 @@ export function configRoutes(fastify: FastifyInstance) { user: username, pass: password, active: true, + clientId: clientId, + clientSecret: clientSecret, }, }); } else { @@ -274,10 +280,73 @@ export function configRoutes(fastify: FastifyInstance) { user: username, pass: password, active: active, + clientId: clientId, + clientSecret: clientSecret, }, }); } + if (serviceType === "gmail") { + const email = await prisma.email.findFirst(); + + const google = new OAuth2Client( + //@ts-expect-error + email?.clientId, + email?.clientSecret, + "http://localhost:3000/admin/smtp/oauth" + ); + + const authorizeUrl = google.generateAuthUrl({ + access_type: "offline", + scope: "https://www.googleapis.com/auth/gmail.send", + }); + + reply.send({ + success: true, + message: "SSO Provider updated!", + authorizeUrl: authorizeUrl, + }); + } + + reply.send({ + success: true, + message: "SSO Provider updated!", + }); + } + } + ); + + fastify.get( + "/api/v1/config/email/oauth/gmail", + + async (request: FastifyRequest, reply: FastifyReply) => { + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { code }: any = request.query; + + console.log(code); + + const email = await prisma.email.findFirst(); + + const google = new OAuth2Client( + //@ts-expect-error + email?.clientId, + email?.clientSecret, + "http://localhost:3000/admin/smtp/oauth" + ); + + const r = await google.getToken(code); + // Make sure to set the credentials on the OAuth2 client. + + await prisma.email.update({ + where: { id: email?.id }, + data: { + refreshToken: r.tokens.refresh_token, + }, + }); + reply.send({ success: true, message: "SSO Provider updated!", diff --git a/apps/api/src/lib/nodemailer/transport.ts b/apps/api/src/lib/nodemailer/transport.ts index 4baa1a2ad..46c6cb726 100644 --- a/apps/api/src/lib/nodemailer/transport.ts +++ b/apps/api/src/lib/nodemailer/transport.ts @@ -1,7 +1,6 @@ import { prisma } from "../../prisma"; const nodemailer = require("nodemailer"); -const { google } = require("google-auth-library"); const { ConfidentialClientApplication } = require("@azure/identity"); export async function createTransportProvider() { @@ -19,13 +18,7 @@ export async function createTransportProvider() { // OAuth2 configuration if (provider?.serviceType === "gmail") { // Gmail - const oAuth2Client = new google.auth.OAuth2( - provider?.clientId, - provider?.clientSecret - ); - oAuth2Client.setCredentials({ refresh_token: provider?.refreshToken }); - const accessToken = await oAuth2Client.getAccessToken(); - + return nodemailer.createTransport({ service: "gmail", auth: { @@ -34,7 +27,7 @@ export async function createTransportProvider() { clientId: provider?.clientId, clientSecret: provider?.clientSecret, refreshToken: provider?.refreshToken, - accessToken: accessToken.token, + // accessToken: accessToken.token, }, }); } else if (provider?.serviceType === "microsoft") { diff --git a/apps/client/@/shadcn/ui/sidebar.tsx b/apps/client/@/shadcn/ui/sidebar.tsx index ac2ab7654..29e5a678c 100644 --- a/apps/client/@/shadcn/ui/sidebar.tsx +++ b/apps/client/@/shadcn/ui/sidebar.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { VariantProps, cva } from "class-variance-authority" diff --git a/apps/client/layouts/adminLayout.tsx b/apps/client/layouts/adminLayout.tsx index ec9eacd6c..50c06bce0 100644 --- a/apps/client/layouts/adminLayout.tsx +++ b/apps/client/layouts/adminLayout.tsx @@ -71,8 +71,8 @@ export default function AdminLayout({ children }: any) { }, { name: "SMTP Email", - href: "/admin/email", - current: location.pathname === "/admin/email", + href: "/admin/smtp", + current: location.pathname === "/admin/smtp", icon: Mailbox, }, { diff --git a/apps/client/pages/admin/email/index.tsx b/apps/client/pages/admin/smtp/index.tsx similarity index 74% rename from apps/client/pages/admin/email/index.tsx rename to apps/client/pages/admin/smtp/index.tsx index 2f9bc9ea4..f1eb7b695 100644 --- a/apps/client/pages/admin/email/index.tsx +++ b/apps/client/pages/admin/smtp/index.tsx @@ -19,6 +19,7 @@ import { import { ExclamationTriangleIcon } from "@heroicons/react/20/solid"; import { notifications } from "@mantine/notifications"; import { getCookie } from "cookies-next"; +import { useRouter } from "next/router"; import { useEffect, useState } from "react"; export default function Notifications() { @@ -169,9 +170,7 @@ export default function Notifications() { Microsoft - - Google - + Google Other @@ -192,7 +191,9 @@ export default function Notifications() { {step === 1 && provider === "microsoft" && ( )} - {step === 1 && provider === "gmail" && } + {step === 1 && provider === "gmail" && ( + + )} {step === 1 && provider === "other" && ( )} @@ -211,8 +212,124 @@ function MicrosoftSettings() { return
Microsoft
; } -function GmailSettings() { - return
Gmail
; +function GmailSettings({ setStep }: { setStep: (step: number) => void }) { + const [clientId, setClientId] = useState(""); + const [clientSecret, setClientSecret] = useState(""); + const [refreshToken, setRefreshToken] = useState(""); + const [user, setUser] = useState(""); + + const router = useRouter(); + + async function submitGmailConfig() { + await fetch(`/api/v1/config/email`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getCookie("session")}`, + }, + body: JSON.stringify({ + host: "smtp.gmail.com", + port: "465", + clientId, + clientSecret, + username: user, + reply: user, + serviceType: "gmail", + }), + }) + .then((res) => res.json()) + .then((res) => { + if (res.success && res.authorizeUrl) { + router.push(res.authorizeUrl); + } + }); + } + + return ( + + + Gmail Settings + Configure your Gmail OAuth2 settings. + + +
+
+
+ +
+ setClientId(e.target.value)} + /> +
+
+ +
+ +
+ setClientSecret(e.target.value)} + /> +
+
+ +
+ +
+ setUser(e.target.value)} + /> +
+
+
+
+
+ + + + +
+ ); } function SMTP({ setStep }: { setStep: (step: number) => void }) { diff --git a/apps/client/pages/admin/smtp/oauth.tsx b/apps/client/pages/admin/smtp/oauth.tsx new file mode 100644 index 000000000..ceb396f90 --- /dev/null +++ b/apps/client/pages/admin/smtp/oauth.tsx @@ -0,0 +1,34 @@ +import { getCookie, setCookie } from "cookies-next"; +import { useRouter } from "next/router"; +import { useEffect } from "react"; +import { useUser } from "../../../store/session"; + +export default function Login() { + const router = useRouter(); + + async function check() { + if (router.query.code) { + await fetch( + `/api/v1/config/email/oauth/gmail?code=${router.query.code}`, + { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getCookie("session")}`, + }, + } + ) + .then((res) => res.json()) + .then((res) => { + if (res.success) { + router.push("/admin/smtp"); + } + }); + } + } + + useEffect(() => { + check(); + }, [router]); + + return
; +} diff --git a/apps/client/pages/admin/email/templates/[id].tsx b/apps/client/pages/admin/smtp/templates/[id].tsx similarity index 100% rename from apps/client/pages/admin/email/templates/[id].tsx rename to apps/client/pages/admin/smtp/templates/[id].tsx diff --git a/apps/client/pages/admin/email/templates/index.tsx b/apps/client/pages/admin/smtp/templates/index.tsx similarity index 100% rename from apps/client/pages/admin/email/templates/index.tsx rename to apps/client/pages/admin/smtp/templates/index.tsx