Added user authentication

This commit is contained in:
2026-07-14 12:59:47 -04:00
parent 5d6aa0a569
commit 6aab99ab8f
4 changed files with 109 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ import { Tabs } from "@heroui/react";
import { Button } from "@heroui/react"; import { Button } from "@heroui/react";
import { useNavigate, useLocation } from "react-router"; import { useNavigate, useLocation } from "react-router";
import porning from "../images/porning.svg"; import porning from "../images/porning.svg";
import User from "./user";
export function Header() { export function Header() {
const location = useLocation(); const location = useLocation();
@@ -42,7 +43,7 @@ export function Header() {
<div className="absolute flex left-1/2 -translate-x-1/2"> <div className="absolute flex left-1/2 -translate-x-1/2">
<img className="h-12" src={porning} alt="Porning logo" /> <img className="h-12" src={porning} alt="Porning logo" />
</div> </div>
<Button>Login</Button> <User />
</Surface> </Surface>
</div> </div>
); );

40
app/components/user.tsx Normal file
View File

@@ -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 <Button onClick={() => login()}>Login</Button>;
}
return (
<Dropdown>
<Button className="pr-0">
<Typography.Paragraph>{user.name}</Typography.Paragraph>
<Avatar>
<Avatar.Image alt="Avatar" src={user.avatar}></Avatar.Image>
</Avatar>
</Button>
<Dropdown.Popover >
<Dropdown.Menu onAction={(key) => handleAction(String(key), logout)}>
<Dropdown.Item id="settings" textValue="Settings">
<Label>Settings</Label>
</Dropdown.Item>
<Separator variant="tertiary"/>
<Dropdown.Item id="logout" textValue="Logout" variant="danger">
<Label>Logout</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>
);
}
function handleAction(key: string, logout: () => Promise<void>) {
if (key === "settings") {
console.log("settings");
} else {
logout();
}
}

63
app/hooks/auth.tsx Normal file
View File

@@ -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<void>
}
const AuthContext = createContext<AuthContextType>({
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 <AuthContext.Provider value={{user, login, logout}}>
{children}
</AuthContext.Provider>
}
export function useAuth() {
return useContext(AuthContext);
}

View File

@@ -9,6 +9,7 @@ import {
import type { Route } from "./+types/root"; import type { Route } from "./+types/root";
import "./app.css"; import "./app.css";
import { AuthProvider } from "./hooks/auth";
export const links: Route.LinksFunction = () => [ export const links: Route.LinksFunction = () => [
{ rel: "preconnect", href: "https://fonts.googleapis.com" }, { rel: "preconnect", href: "https://fonts.googleapis.com" },
@@ -33,7 +34,9 @@ export function Layout({ children }: { children: React.ReactNode }) {
<Links /> <Links />
</head> </head>
<body className="bg-background text-foreground"> <body className="bg-background text-foreground">
{children} <AuthProvider>
{children}
</AuthProvider>
<ScrollRestoration /> <ScrollRestoration />
<Scripts /> <Scripts />
</body> </body>