From 5e71e1eeeb40245aea4c13d1759a96078c6a746e Mon Sep 17 00:00:00 2001 From: Varun Raj Date: Tue, 3 Sep 2024 08:11:25 +0530 Subject: [PATCH] feat: setup auth flow to work in parallel with api key Signed-off-by: Varun Raj --- bun.lockb | Bin 382283 -> 386528 bytes package.json | 2 + src/components/auth/LoginForm.tsx | 88 ++++++++++++++++++ src/components/layouts/RootLayout.tsx | 8 +- src/components/ui/card.tsx | 6 +- src/config/app.config.ts | 6 ++ src/config/environment.ts | 1 + src/config/routes.ts | 4 +- src/handlers/api/user.handler.ts | 6 +- src/handlers/serverUtils/user.utils.ts | 55 ++++++++++- src/lib/api.ts | 18 +--- src/lib/cookie.ts | 30 ++++++ src/lib/jwt.ts | 16 ++++ .../api/albums/potential-albums-assets.ts | 2 +- .../api/albums/potential-albums-dates.ts | 2 +- src/pages/api/analytics/exif/[property].ts | 2 +- .../api/assets/missing-location-assets.ts | 2 +- .../api/assets/missing-location-dates.ts | 2 +- src/pages/api/immich-proxy/thumbnail/[id].ts | 11 ++- src/pages/api/people/[id]/similar-faces.ts | 2 +- src/pages/api/people/list.ts | 2 +- src/pages/api/users/login.ts | 36 +++++++ src/pages/api/users/me.ts | 25 +++++ 23 files changed, 296 insertions(+), 30 deletions(-) create mode 100644 src/components/auth/LoginForm.tsx create mode 100644 src/config/app.config.ts create mode 100644 src/lib/cookie.ts create mode 100644 src/lib/jwt.ts create mode 100644 src/pages/api/users/login.ts create mode 100644 src/pages/api/users/me.ts diff --git a/bun.lockb b/bun.lockb index 422f823944880dd8ec4f6ccbc9a1e98f7f49fca5..9d2a89a804f999f712da50a87d22232eb4c837a4 100755 GIT binary patch delta 77621 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 delta 75394 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 O+%vb`m3qhAM*n{-Z>X67 diff --git a/package.json b/package.json index 81dac4b..03caf4c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "cmdk": "1.0.0", "date-fns": "^3.6.0", "drizzle-orm": "^0.33.0", + "jsonwebtoken": "^9.0.2", "lucide-react": "^0.428.0", "next": "14.2.5", "next-themes": "^0.3.0", @@ -56,6 +57,7 @@ "@semantic-release/github": "^10.1.7", "@semantic-release/npm": "^12.0.1", "@semantic-release/release-notes-generator": "^14.0.1", + "@types/jsonwebtoken": "^9.0.6", "@types/node": "^20", "@types/pg": "^8.11.6", "@types/react": "^18", diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx new file mode 100644 index 0000000..b2d8ce2 --- /dev/null +++ b/src/components/auth/LoginForm.tsx @@ -0,0 +1,88 @@ +import Link from "next/link" + +import { Button } from "@/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useState } from "react" +import { loginUser } from "@/handlers/api/user.handler" + +export const description = + "A login form with email and password. There's an option to login with Google and a link to sign up if you don't have an account." + +export function LoginForm() { + + const [formData, setFormData] = useState({ + email: "", + password: "", + }) + const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + + const handleChange = (e: React.ChangeEvent) => { + setFormData({ ...formData, [e.target.id]: e.target.value }) + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + setLoading(true) + loginUser(formData.email, formData.password).then((user) => { + console.log(user) + }).catch((error) => { + setErrorMessage(error.message) + }).finally(() => { + setLoading(false) + }) + } + + + return ( + + + Login + + Enter your email below to login to your account + + + +
+
+ + +
+
+
+ + + Forgot your password? + +
+ +
+ + +
+ +
+
+ ) +} diff --git a/src/components/layouts/RootLayout.tsx b/src/components/layouts/RootLayout.tsx index bf6e093..0055795 100644 --- a/src/components/layouts/RootLayout.tsx +++ b/src/components/layouts/RootLayout.tsx @@ -8,6 +8,7 @@ import ErrorBlock from "../shared/ErrorBlock"; import Loader from "../ui/loader"; import { ArrowRight, TriangleAlert } from "lucide-react"; import Link from "next/link"; +import { LoginForm } from "../auth/LoginForm"; type RootLayoutProps = { children: ReactNode; @@ -25,7 +26,8 @@ export default function RootLayout({ children }: RootLayoutProps) { return getMe() .then(setUser) .catch((error) => { - setErrorMessage(error); + if (error.status === 403) return setUser(null); + return setErrorMessage(error); }) .finally(() => setLoading(false)); }; @@ -36,6 +38,7 @@ export default function RootLayout({ children }: RootLayoutProps) { if (loading) return ; if (errorMessage) + return (
); - if (!user) return null; + + if (!user) return ; return ( diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 3c9063a..c0ac4db 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -76,12 +76,12 @@ CardFooter.displayName = "CardFooter" interface CardProps { title?: string description?: string - + className?: string children: React.ReactNode } -const Card = ({ children, title, description }: CardProps) => { +const Card = ({ children, title, description, className }: CardProps) => { return ( - + {!!(title || description) && ( {title && {title}} diff --git a/src/config/app.config.ts b/src/config/app.config.ts new file mode 100644 index 0000000..749a616 --- /dev/null +++ b/src/config/app.config.ts @@ -0,0 +1,6 @@ +import { ENV } from "./environment"; + +export const appConfig = { + sessionCookieName: 'power-tools-session', + jwtToken: ENV.JWT_SECRET || "jwt-secret", // This is a fallback value, it should be replaced with a proper secret +} \ No newline at end of file diff --git a/src/config/environment.ts b/src/config/environment.ts index f0972b5..3062eda 100644 --- a/src/config/environment.ts +++ b/src/config/environment.ts @@ -3,6 +3,7 @@ export const ENV = { EXTERNAL_IMMICH_URL: (process.env.EXTERNAL_IMMICH_URL || process.env.IMMICH_URL) as string, IMMICH_API_KEY: process.env.IMMICH_API_KEY as string, DATABASE_URL: (process.env.DATABASE_URL || `postgresql://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${(process.env.DB_HOST || 'immich_postgres')}:${(process.env.DB_PORT || '5432')}/${process.env.DB_DATABASE_NAME}`), + JWT_SECRET: process.env.JWT_SECRET as string, }; diff --git a/src/config/routes.ts b/src/config/routes.ts index 2d59e96..2f55c49 100644 --- a/src/config/routes.ts +++ b/src/config/routes.ts @@ -1,7 +1,9 @@ export const BASE_API_ENDPOINT = '/api'; export const BASE_PROXY_ENDPOINT = BASE_API_ENDPOINT + '/immich-proxy'; -export const GET_ME_PATH = BASE_PROXY_ENDPOINT + "/users/me"; +export const GET_ME_PATH = BASE_API_ENDPOINT + "/users/me"; +export const LOGIN_PATH = BASE_API_ENDPOINT + "/users/login"; + export const LIST_PEOPLE_PATH = BASE_API_ENDPOINT + "/people/list"; export const SEARCH_PEOPLE_PATH = BASE_PROXY_ENDPOINT + "/search/person"; diff --git a/src/handlers/api/user.handler.ts b/src/handlers/api/user.handler.ts index 534de92..32c8c55 100644 --- a/src/handlers/api/user.handler.ts +++ b/src/handlers/api/user.handler.ts @@ -1,6 +1,10 @@ -import { GET_ME_PATH } from "@/config/routes"; +import { GET_ME_PATH, LOGIN_PATH } from "@/config/routes"; import API from "@/lib/api"; export const getMe = async () => { return API.get(GET_ME_PATH); +} + +export const loginUser = async (email: string, password: string) => { + return API.post(LOGIN_PATH, { email, password }); } \ No newline at end of file diff --git a/src/handlers/serverUtils/user.utils.ts b/src/handlers/serverUtils/user.utils.ts index fd46860..a47eed2 100644 --- a/src/handlers/serverUtils/user.utils.ts +++ b/src/handlers/serverUtils/user.utils.ts @@ -1,6 +1,13 @@ +import { appConfig } from "@/config/app.config" import { ENV } from "@/config/environment" +import { getCookie } from "@/lib/cookie" +import { decodeJWT } from "@/lib/jwt" +import { NextApiRequest } from "next" + + +export const getCurrentUserFromAPIKey = () => { + if (!ENV.IMMICH_URL || !ENV.IMMICH_API_KEY) return null; -export const getCurrentUser = () => { return fetch(ENV.IMMICH_URL + "/api/users/me", { headers: { 'x-api-key': ENV.IMMICH_API_KEY, @@ -10,6 +17,50 @@ export const getCurrentUser = () => { if (res.ok) { return res.json() } - throw new Error("Failed to fetch user") + return null }) +} + +export const getCurrentUserFromAccessToken = (token: string) => { + return fetch(ENV.IMMICH_URL + "/api/users/me", { + headers: { + Authorization: `Bearer ${token}`, + Accept: "application/json", + }, + }).then((res) => { + if (res.ok) { + return res.json() + } + return null + }) +} + +export const getCurrentUser = async (req: NextApiRequest) => { + const session = getCookie(req, appConfig.sessionCookieName) + + if (session) { + const user = await getCurrentUserFromAccessToken(session) + return { + ...user, + accessToken: session, + } + } + + return getCurrentUserFromAPIKey() +} + +export const loginUser = async (email: string, password: string) => { + const res = await fetch(ENV.IMMICH_URL + "/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }) + + if (res.ok) { + return res.json() + } + + return null } \ No newline at end of file diff --git a/src/lib/api.ts b/src/lib/api.ts index bb00bf0..0483d40 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -7,23 +7,15 @@ const handleErrors = (error: AxiosError | any) => { if (error.code === "ERR_BAD_RESPONSE") { return Promise.reject({ error: "Unable to process this request, please try again later", - message: error.code + message: error.code, }); } let errorObject = {} if (error.response) { - const { status } = error.response - if (status === 422) { - errorObject = { - ...error.response.data, - message: 'Unable to process this request, please try again later', - } - } else { - errorObject = { - ...error.response.data, - } - } - return Promise.reject(errorObject) + return Promise.reject({ + ...error.response.data, + status: error.response.status, + }) } errorObject = { message: 'Network Error. Please check your connection and try again!', diff --git a/src/lib/cookie.ts b/src/lib/cookie.ts new file mode 100644 index 0000000..2174af5 --- /dev/null +++ b/src/lib/cookie.ts @@ -0,0 +1,30 @@ +import type { CookieSerializeOptions } from 'cookie' +import cookie from 'cookie' +import type { IncomingMessage } from 'http' +import type { NextApiRequest } from 'next' + +export const getCookie = ( + req: NextApiRequest | IncomingMessage, + name: string +) => { + const cookieData = cookie.parse(req.headers.cookie || '') + if (cookieData && cookieData[name]) { + return cookieData[name] + } + return null +} + +export const serializeCookie = ( + name: string, + value: string, + options: CookieSerializeOptions = {} +) => { + return cookie.serialize(name, String(value), { + httpOnly: true, + maxAge: 60 * 60 * 24 * 7, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + path: '/', + ...options, + }) +} \ No newline at end of file diff --git a/src/lib/jwt.ts b/src/lib/jwt.ts new file mode 100644 index 0000000..3dd0375 --- /dev/null +++ b/src/lib/jwt.ts @@ -0,0 +1,16 @@ +import jwt from 'jsonwebtoken' + +export const decodeJWT = (token: string, key: string): any => { + try { + const payload = jwt.verify(token, key, { + ignoreExpiration: true + }) + return payload + } catch (error) { + return null + } +} + +export const encodeJWT = (payload: any, key: string): string => { + return jwt.sign(payload, key) +} diff --git a/src/pages/api/albums/potential-albums-assets.ts b/src/pages/api/albums/potential-albums-assets.ts index 4ab957f..555919a 100644 --- a/src/pages/api/albums/potential-albums-assets.ts +++ b/src/pages/api/albums/potential-albums-assets.ts @@ -46,7 +46,7 @@ export default async function handler( res: NextApiResponse ) { try { - const currentUser = await getCurrentUser() + const currentUser = await getCurrentUser(req) const { startDate } = req.query as { startDate: string }; const { rows } = await db.execute(SELECT_ORPHAN_PHOTOS(startDate, currentUser.id)); return res.status(200).json(rows); diff --git a/src/pages/api/albums/potential-albums-dates.ts b/src/pages/api/albums/potential-albums-dates.ts index 0108b4c..45af737 100644 --- a/src/pages/api/albums/potential-albums-dates.ts +++ b/src/pages/api/albums/potential-albums-dates.ts @@ -28,7 +28,7 @@ export default async function handler( res: NextApiResponse ) { try { - const currentUser = await getCurrentUser() + const currentUser = await getCurrentUser(req) const { rows } = await db.execute(SELECT_ORPHAN_PHOTOS(currentUser.id)); return res.status(200).json(rows); } catch (error: any) { diff --git a/src/pages/api/analytics/exif/[property].ts b/src/pages/api/analytics/exif/[property].ts index cb30ba3..13e99ed 100644 --- a/src/pages/api/analytics/exif/[property].ts +++ b/src/pages/api/analytics/exif/[property].ts @@ -34,7 +34,7 @@ export default async function handler( } try { - const currentUser = await getCurrentUser(); + const currentUser = await getCurrentUser(req); const dataFromDB = await db.select({ value: count(), label: column, diff --git a/src/pages/api/assets/missing-location-assets.ts b/src/pages/api/assets/missing-location-assets.ts index 1594cf4..56833b2 100644 --- a/src/pages/api/assets/missing-location-assets.ts +++ b/src/pages/api/assets/missing-location-assets.ts @@ -15,7 +15,7 @@ export default async function handler( startDate: string; }; - const currentUser = await getCurrentUser(); + const currentUser = await getCurrentUser(req); if (!startDate) { return res.status(400).json({ diff --git a/src/pages/api/assets/missing-location-dates.ts b/src/pages/api/assets/missing-location-dates.ts index 1680d7f..01eea33 100644 --- a/src/pages/api/assets/missing-location-dates.ts +++ b/src/pages/api/assets/missing-location-dates.ts @@ -11,7 +11,7 @@ export default async function handler( res: NextApiResponse ) { try { - const currentUser = await getCurrentUser(); + const currentUser = await getCurrentUser(req); const rows = await db .select({ asset_count: desc(count(assets.id)), diff --git a/src/pages/api/immich-proxy/thumbnail/[id].ts b/src/pages/api/immich-proxy/thumbnail/[id].ts index e4042fe..5fb0edb 100644 --- a/src/pages/api/immich-proxy/thumbnail/[id].ts +++ b/src/pages/api/immich-proxy/thumbnail/[id].ts @@ -1,6 +1,7 @@ // pages/api/proxy.js import { ENV } from '@/config/environment'; +import { getCurrentUser } from '@/handlers/serverUtils/user.utils'; import { NextApiRequest, NextApiResponse } from 'next' export const config = { @@ -17,13 +18,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { id } = req.query; const targetUrl = `${ENV.IMMICH_URL}/api/people/${id}/thumbnail`; + const currentUser = await getCurrentUser(req); + + if (!currentUser) { + return res.status(403).json({ message: 'Forbidden' }) + } + + console.log('currentUser.accessToken:', currentUser) + try { // Forward the request to the target API const response = await fetch(targetUrl, { method: 'GET', headers: { 'Content-Type': 'application/octet-stream', - 'x-api-key': ENV.IMMICH_API_KEY, + Authorization: `Bearer ${currentUser.accessToken}`, }, }) diff --git a/src/pages/api/people/[id]/similar-faces.ts b/src/pages/api/people/[id]/similar-faces.ts index ff3e679..599b646 100644 --- a/src/pages/api/people/[id]/similar-faces.ts +++ b/src/pages/api/people/[id]/similar-faces.ts @@ -34,7 +34,7 @@ export default async function handler( sortOrder = "desc", } = req.query as any as IQuery; - const currentUser = await getCurrentUser(); + const currentUser = await getCurrentUser(req); const personRecords = await db .select() .from(person) diff --git a/src/pages/api/people/list.ts b/src/pages/api/people/list.ts index d21b504..4885d5b 100644 --- a/src/pages/api/people/list.ts +++ b/src/pages/api/people/list.ts @@ -44,7 +44,7 @@ export default async function handler( sortOrder = "desc", } = req.query as any as IQuery; - const currentUser = await getCurrentUser(); + const currentUser = await getCurrentUser(req); const maximumAssetCount = !maxValue || maxValue <= 0 ? 1000000 : maxValue; const whereClause = and( diff --git a/src/pages/api/users/login.ts b/src/pages/api/users/login.ts new file mode 100644 index 0000000..9410e7d --- /dev/null +++ b/src/pages/api/users/login.ts @@ -0,0 +1,36 @@ +// pages/api/proxy.js + +import { appConfig } from "@/config/app.config"; +import { getCurrentUser, loginUser } from "@/handlers/serverUtils/user.utils"; +import { serializeCookie } from "@/lib/cookie"; +import { NextApiRequest, NextApiResponse } from "next"; + +export const config = { + api: { + bodyParser: true, + }, +}; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).json({ message: "Method Not Allowed" }); + } + + const { email, password } = req.body; + if (!email || !password) { + return res.status(400).json({ message: "Email and password are required" }); + } + + const user = await loginUser(email, password); + if (user) { + res.setHeader("Set-Cookie", serializeCookie(appConfig.sessionCookieName, user.accessToken)); + return res.status(200).json(user); + } + else { + return res.status(403).json({ message: "Invalid User Credentials" }); + } + +} diff --git a/src/pages/api/users/me.ts b/src/pages/api/users/me.ts new file mode 100644 index 0000000..38b8496 --- /dev/null +++ b/src/pages/api/users/me.ts @@ -0,0 +1,25 @@ +// pages/api/proxy.js + +import { getCurrentUser } from "@/handlers/serverUtils/user.utils"; +import { NextApiRequest, NextApiResponse } from "next"; + +export const config = { + api: { + bodyParser: false, + }, +}; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "GET") + + return res.status(405).json({ message: "Method Not Allowed" }); + + const user = getCurrentUser(req); + + if (user) return res.status(200).json(user); + + return res.status(403).json({ message: "Forbidden" }); +}