From 6aab99ab8f87b63f7b4ad3544e8ef9ff9a1484ab Mon Sep 17 00:00:00 2001 From: AINDUSTRIES Date: Tue, 14 Jul 2026 12:59:47 -0400 Subject: [PATCH] Added user authentication --- app/components/header.tsx | 3 +- app/components/user.tsx | 40 +++++++++++++++++++++++++ app/hooks/auth.tsx | 63 +++++++++++++++++++++++++++++++++++++++ app/root.tsx | 5 +++- 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 app/components/user.tsx create mode 100644 app/hooks/auth.tsx diff --git a/app/components/header.tsx b/app/components/header.tsx index 53964da..f8659a5 100644 --- a/app/components/header.tsx +++ b/app/components/header.tsx @@ -3,6 +3,7 @@ import { Tabs } from "@heroui/react"; import { Button } from "@heroui/react"; import { useNavigate, useLocation } from "react-router"; import porning from "../images/porning.svg"; +import User from "./user"; export function Header() { const location = useLocation(); @@ -42,7 +43,7 @@ export function Header() {
Porning logo
- + ); diff --git a/app/components/user.tsx b/app/components/user.tsx new file mode 100644 index 0000000..2a68017 --- /dev/null +++ b/app/components/user.tsx @@ -0,0 +1,40 @@ +import { Button, Avatar, Typography, Dropdown, Label, Separator } from "@heroui/react"; +import { useAuth } from "~/hooks/auth"; + +export default function User() { + const {user, login, logout} = useAuth(); + + if (user === null) { + return ; + } + + return ( + + + + handleAction(String(key), logout)}> + + + + + + + + + + + ); +} + +function handleAction(key: string, logout: () => Promise) { + if (key === "settings") { + console.log("settings"); + } else { + logout(); + } +} \ No newline at end of file diff --git a/app/hooks/auth.tsx b/app/hooks/auth.tsx new file mode 100644 index 0000000..cf629c9 --- /dev/null +++ b/app/hooks/auth.tsx @@ -0,0 +1,63 @@ +import { useEffect, useState, createContext, useContext, type PropsWithChildren } from "react"; +import { useSearchParams } from "react-router"; + +type User = { + id: string, + steamid: string, + name: string, + avatar: string +} + +type AuthContextType = { + user: User | null, + login: () => void, + logout: () => Promise +} + +const AuthContext = createContext({ + user: null, + login: () => {}, + logout: async () => {} +}); + +export function AuthProvider({children}: PropsWithChildren) { + const [user, setUser] = useState(null); + const [searchParams, setSeachParams] = useSearchParams(); + + useEffect(() => { + const load = async () => { + + if (searchParams.get("openid.ns") !== null) { + let response = await fetch("http://localhost:8080/login?" + searchParams.toString()).then(r => r.text()); + setSeachParams({}, {replace: true}); + if (response === "nok") { + // TODO: signal error to user + } + } + + let data = await fetch("http://localhost:8080/me", {credentials: "include"}) + .then(r => r.json()) + + console.log(data); + setUser(data); + } + + load(); + }, []); + + const login = () => { + location.replace("https://steamcommunity.com/openid/login?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=http://localhost:5173&openid.return_to=http://localhost:5173"); + }; + + const logout = async () => { + setUser(null); + }; + + return + {children} + +} + +export function useAuth() { + return useContext(AuthContext); +} \ No newline at end of file diff --git a/app/root.tsx b/app/root.tsx index 63f4b0d..9c96f36 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -9,6 +9,7 @@ import { import type { Route } from "./+types/root"; import "./app.css"; +import { AuthProvider } from "./hooks/auth"; export const links: Route.LinksFunction = () => [ { rel: "preconnect", href: "https://fonts.googleapis.com" }, @@ -33,7 +34,9 @@ export function Layout({ children }: { children: React.ReactNode }) { - {children} + + {children} +