diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 60106fd..2ca430b 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -63,11 +63,17 @@ type Manufacturer {
   notice   String?
 }
 
+type Photo {
+  name String
+  data Bytes
+}
+
 model AuditoryResource {
   id              String         @id @default(auto()) @map("_id") @db.ObjectId
   icon            String
   name            String
   description     String
+  photo           Photo?
   manufacturer    Manufacturer?
   ages            RangeInput
   skills          Skill[]
diff --git a/src/components/ResourceTable.tsx b/src/components/ResourceTable.tsx
index a61f5b3..a4c40b3 100644
--- a/src/components/ResourceTable.tsx
+++ b/src/components/ResourceTable.tsx
@@ -9,12 +9,39 @@ import { ClipboardDocumentListIcon } from "@heroicons/react/24/outline";
 import Image from "next/image";
 import Link from "next/link";
 import { translateEnumPlatform, translateEnumSkill } from "~/utils/enumWordLut";
-import { type ChangeEvent } from "react";
+import { useEffect, type ChangeEvent, useState } from "react";
 import { ChevronDownIcon } from "@heroicons/react/24/outline";
 import { type ParsedUrlQuery, type ParsedUrlQueryInput } from "querystring";
 import { useRouter } from "next/router";
 import { PriceIcon } from "~/prices/Icons";
 
+export const ResourcePhoto = ({ resource }: { resource: AuditoryResource }) => {
+  const [blobSrc, setBlobSrc] = useState<string | undefined>(undefined);
+
+  useEffect(() => {
+    if (!resource.photo?.data) {
+      return;
+    }
+
+    const blob = new Blob([resource.photo.data], { type: "image/png" });
+    setBlobSrc(URL.createObjectURL(blob));
+  }, [resource.photo]);
+
+  const commonProps = {
+    width: 512,
+    height: 512,
+  };
+
+  return (
+    <Image
+      className="w-full rounded-xl border border-neutral-400 bg-white drop-shadow-lg"
+      src={blobSrc ?? `/resource_logos/${resource.icon}`}
+      alt={`${resource.name} logo`}
+      {...commonProps}
+    />
+  );
+};
+
 export const ResourceInfo = ({
   resource,
   showMoreInfo,
@@ -46,13 +73,7 @@ export const ResourceInfo = ({
         {showMoreInfo ? (
           <Link href={`resources/${resource.id}`}>
             <div className="flex w-20 flex-col justify-center space-y-2 sm:w-28">
-              <Image
-                className="w-full rounded-xl border border-neutral-400 bg-white drop-shadow-lg"
-                src={`/resource_logos/${resource.icon}`}
-                alt={`${resource.name} logo`}
-                width={512}
-                height={512}
-              />
+              <ResourcePhoto resource={resource} />
               <span className="block rounded-lg border border-neutral-900 bg-neutral-900 py-[1px] text-center text-white hover:bg-neutral-500 print:hidden">
                 more info
               </span>
diff --git a/src/pages/api/resources/photo/[id].tsx b/src/pages/api/resources/photo/[id].tsx
index 3257ea3..73dfb12 100644
--- a/src/pages/api/resources/photo/[id].tsx
+++ b/src/pages/api/resources/photo/[id].tsx
@@ -1,7 +1,9 @@
 import { type NextApiHandler } from "next";
 import formidable from "formidable";
-import * as path from "path";
+import * as fs from "fs";
 import { prisma } from "~/server/db";
+import { getServerAuthSession } from "~/server/auth";
+import { Role } from "@prisma/client";
 
 /**
  * Returns filename for a given filepath.
@@ -17,6 +19,12 @@ const handler: NextApiHandler = async (req, res) => {
     return;
   }
 
+  const authSession = await getServerAuthSession({ req, res });
+  if (!authSession?.user || authSession.user.role !== Role.ADMIN) {
+    res.writeHead(401, "Not authorized");
+    return;
+  }
+
   const { id } = req.query;
 
   if (Array.isArray(id) || !id) {
@@ -29,17 +37,21 @@ const handler: NextApiHandler = async (req, res) => {
     keepExtensions: true,
   });
 
-  const localUploadPath: Promise<string> = new Promise((resolve, reject) => {
-    form.parse(req, (_err, _fields, files) => {
-      const photo = Array.isArray(files.photo) ? files.photo[0] : files.photo;
-      if (!photo) {
-        reject("Invalid file type sent (or none provided)");
-        return;
-      }
+  const uploadPhoto: Promise<formidable.File> = new Promise(
+    (resolve, reject) => {
+      form.parse(req, (_err, _fields, files) => {
+        const photo = Array.isArray(files.photo) ? files.photo[0] : files.photo;
+        if (!photo) {
+          reject("Invalid file type sent (or none provided)");
+          return;
+        }
 
-      resolve(path.join("uploads", getFileName(photo.filepath)));
-    });
-  });
+        resolve(photo);
+      });
+    }
+  );
+
+  const photoBuffer = fs.readFileSync((await uploadPhoto).filepath);
 
   try {
     await prisma.auditoryResource.update({
@@ -47,7 +59,12 @@ const handler: NextApiHandler = async (req, res) => {
         id,
       },
       data: {
-        icon: await localUploadPath,
+        photo: {
+          name: getFileName(
+            (await uploadPhoto).originalFilename ?? "resource ICON"
+          ),
+          data: photoBuffer,
+        },
       },
     });
   } catch (error: unknown) {
diff --git a/src/pages/resources/[id]/edit.tsx b/src/pages/resources/[id]/edit.tsx
index 3ee4ea7..a8d9d15 100644
--- a/src/pages/resources/[id]/edit.tsx
+++ b/src/pages/resources/[id]/edit.tsx
@@ -60,9 +60,6 @@ const EditResourcePage = (
   });
 
   const onSubmit: SubmitHandler<ResourceUpdateInput> = async (data) => {
-    // TODO: Fix file upload, currently it is not updating correctly on the server side
-    // May also need to look into re-rendering static pages when icon changes
-    // Also need to add authentication of route!
     if (updateIconFile) {
       const data = new FormData();
       data.append("photo", updateIconFile);
@@ -75,8 +72,6 @@ const EditResourcePage = (
         }
       );
 
-      console.log("uploading icon");
-
       if (uploadResponse.status !== 200) {
         setServerError(
           "Failed uploading resource icon file. Changes did not save!"
diff --git a/src/server/api/routers/auditoryResources.ts b/src/server/api/routers/auditoryResources.ts
index 4ab1e86..1252dae 100644
--- a/src/server/api/routers/auditoryResources.ts
+++ b/src/server/api/routers/auditoryResources.ts
@@ -137,6 +137,7 @@ export const auditoryResourceRouter = createTRPCRouter({
         }),
       ]);
 
+      // TODO: The issue here is the photo binary data can't be sent over tRPC which will cause the request to be unparsable by the client
       return {
         count,
         resources,
diff --git a/src/utils/api.ts b/src/utils/api.ts
index f4f4ad5..984777a 100644
--- a/src/utils/api.ts
+++ b/src/utils/api.ts
@@ -17,6 +17,15 @@ const getBaseUrl = () => {
   return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
 };
 
+superjson.registerCustom<Buffer, number[]>(
+  {
+    isApplicable: (v): v is Buffer => v instanceof Buffer,
+    serialize: (v) => [...v],
+    deserialize: (v) => Buffer.from(v),
+  },
+  "buffer"
+);
+
 /** A set of type-safe react-query hooks for your tRPC API. */
 export const api = createTRPCNext<AppRouter>({
   config() {