Skip to content

Commit

Permalink
feat: the beginnings of steam auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Shigbeard committed Aug 5, 2024
1 parent 4d82478 commit 9be68fd
Show file tree
Hide file tree
Showing 11 changed files with 435 additions and 60 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
## Getting Started

```shellscript
npm install
npm install --legacy-peer-deps
```

You need to include --legacy-peer-deps because [remix-auth-steam](https://github.com/Andreychik32/remix-auth-steam) is largely unmaintained.

## Development

Run the dev server:
Expand All @@ -17,6 +19,18 @@ Run the dev server:
npm run dev
```

Note - For steam logins you need an API key. You can get one [here](https://steamcommunity.com/dev/apikey).

## Env Variables

```shellscript
# .env
# Required for steam login
STEAM_API_KEY=your_steam_api_key
# Informs the application where to redirect after login. NO TRAILING SLASH
BASE_URL=http://localhost:5173
```

## Deployment

First, build your app for production:
Expand Down
16 changes: 16 additions & 0 deletions app/auth.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Authenticator } from "remix-auth";
import { steamSessionStorage } from "./sessions.server";
import { SteamStrategy, SteamStrategyVerifyParams } from "remix-auth-steam";

export type User = SteamStrategyVerifyParams;

export const authenticator = new Authenticator<User>(steamSessionStorage);

authenticator.use(
new SteamStrategy(
{
returnURL: `${process.env.BASE_URL || 'http://localhost:5173'}/auth/steam/callback`,
apiKey: process.env.STEAM_API_KEY! as string,
}, async (user) => user
)
);
23 changes: 19 additions & 4 deletions app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import clsx from "clsx";
import React from 'react';
import { useLocation } from '@remix-run/react';
import { useLocation, useNavigate } from '@remix-run/react';
import { Theme, ThemeProvider, useTheme } from 'remix-themes';
import type { User } from "~/auth.server"
import { SerializeFrom } from "@remix-run/node";

export interface NavbarItemProps {
href: string;
label: string;
Expand All @@ -10,10 +13,12 @@ export interface NavbarItemProps {
export interface NavbarProps {
siteTitle?: string;
items?: NavbarItemProps[];
user: SerializeFrom<User> | null;
}

export const Navbar: React.FC<NavbarProps> = (props) => {
// Component logic goes here
const navigate = useNavigate();
const currentpage = useLocation().pathname;
const [menuOpen, setMenuOpen] = React.useState(false);
const toggleMenu = () => {
Expand All @@ -27,7 +32,6 @@ export const Navbar: React.FC<NavbarProps> = (props) => {
setMenuOpen(false);
}
const [theme, setTheme] = useTheme();
console.log(theme);
const toggleTheme = () => {
switch (theme) {
case Theme.LIGHT:
Expand All @@ -42,6 +46,15 @@ export const Navbar: React.FC<NavbarProps> = (props) => {
}
}

const handleAuth = () => {
if (props.user) {
navigate('/auth/logout');
return;
}
navigate('/auth/steam');
return
}

return (
// JSX markup for your component goes here
<ThemeProvider specifiedTheme={theme} themeAction="/action/set-theme">
Expand Down Expand Up @@ -97,8 +110,8 @@ export const Navbar: React.FC<NavbarProps> = (props) => {
<div className={profileOpen ? "w-full md:block md:w-auto md:space-x-8" : "w-full md:block md:w-auto md:space-x-8 hidden"} id="profile-menu">
<ul className='flex flex-col mt-4 md:flex-row md:space-x-8 md:mt-0 md:text-sm md:font-medium'>
<li>
<button type="button" className='sm:w-full sm:text-left block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-gray-400 md:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700'>
Not Signed In - Sign In Coming Soon!
<button onClick={handleAuth} type="button" className='sm:w-full sm:text-left block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-gray-400 md:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700'>
{props.user ? props.user.nickname : 'Login'}
</button>
</li>
</ul>
Expand All @@ -121,3 +134,5 @@ export const Navbar: React.FC<NavbarProps> = (props) => {
</ThemeProvider>
);
};


35 changes: 6 additions & 29 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,17 @@ import { themeSessionResolver } from "./sessions.server";
import { Navbar } from "~/components/Navbar";
import { LoaderFunctionArgs } from "@remix-run/node";

import { authenticator } from "~/auth.server";

export async function loader({ request }: LoaderFunctionArgs) {
const { getTheme } = await themeSessionResolver(request)
const auth = await authenticator.isAuthenticated(request);
return {
theme: getTheme(),
auth: auth,
}
}

// export function Layout({ children }: { children: React.ReactNode }) {
// const data = useLoaderData<typeof loader>();
// const [theme] = useTheme();
// const pages = [
// { href: "/", label: "Home" },
// { href: "//bans.triumphtf2.com", label: "Bans" },
// { href: "//leaderboards.triumphtf2.com", label: "Leaderboards" },
// ]
// return (
// <html lang="en">
// <head>
// <meta charSet="utf-8" />
// <meta name="viewport" content="width=device-width, initial-scale=1" />
// <Meta />
// <Links />
// </head>
// <body>
// <Navbar siteTitle="TriumphTF2" items={pages} />
// {children}
// <ScrollRestoration />
// <Scripts />
// </body>
// </html>
// );
// }

export function App() {
const data = useLoaderData<typeof loader>();
const [theme] = useTheme();
Expand All @@ -66,7 +44,7 @@ export function App() {
<Links />
</head>
<body className="bg-background dark:bg-foreground">
<Navbar siteTitle="TriumphTF2" items={pages} />
<Navbar siteTitle="TriumphTF2" items={pages} user={data.auth} />
<Outlet />
<ScrollRestoration />
<Scripts />
Expand All @@ -79,8 +57,7 @@ export default function AppWithProviders() {
const data = useLoaderData<typeof loader>()
return (
<ThemeProvider specifiedTheme={data.theme} themeAction="/action/set-theme">
{/* <PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} /> */}
<App></App>
<App />
</ThemeProvider>
)
}
6 changes: 6 additions & 0 deletions app/routes/auth.logout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticator } from "~/auth.server";

export async function loader({request}: LoaderFunctionArgs) {
await authenticator.logout(request, { redirectTo: '/' });
}
9 changes: 9 additions & 0 deletions app/routes/auth.steam.callback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticator } from "~/auth.server";

export async function loader({request}: LoaderFunctionArgs) {
return authenticator.authenticate('steam', request, {
successRedirect: '/dashboard',
failureRedirect: '/',
});
}
6 changes: 6 additions & 0 deletions app/routes/auth.steam.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticator } from "~/auth.server";

export async function loader({request}: LoaderFunctionArgs) {
await authenticator.authenticate('steam', request, {});
}
38 changes: 38 additions & 0 deletions app/routes/dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { Link, useLoaderData, useNavigate, type MetaFunction } from "@remix-run/react";
import { authenticator } from "~/auth.server";

export const meta: MetaFunction = () => {
return [
{ title: 'TriumphTF2 - Dashboard' },
{ description: 'Your dashboard' },
];
};

export async function loader({request}: LoaderFunctionArgs) {
const auth = await authenticator.isAuthenticated(request);
if (auth == null) {
await authenticator.authenticate('steam', request, {
successRedirect: '/dashboard',
failureRedirect: '/',
});
}
return {auth: auth};
}

export default function Dashboard() {
const data = useLoaderData<typeof loader>();
const navigate = useNavigate();
if (data.auth == null) {
navigate('/');
return <></>;
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {data.auth.nickname}!</p>
<p>This is a placeholder page. More content to come!</p>
<Link to="/auth/logout">Logout</Link> - <Link to="/">Home</Link>
</div>
);
}
18 changes: 17 additions & 1 deletion app/sessions.server.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createThemeSessionResolver } from "remix-themes"
import { createCookieSessionStorage } from "@remix-run/node"


// You can default to 'development' if process.env.NODE_ENV is not set
const isProduction = process.env.NODE_ENV === "production"

Expand All @@ -15,7 +16,22 @@ const sessionStorage = createCookieSessionStorage({
...(isProduction
? { domain: "triumphtf2.com", secure: true }
: {}),
},
}
})

export const steamSessionStorage = createCookieSessionStorage({
cookie: {
name: "_session",
path: "/",
sameSite: "lax",
httpOnly: true,
secrets: ["its845andtheyhavenotreadiedupyet"],
// Set domain and secure only if in production
...(isProduction
? { domain: "triumphtf2.com", secure: true }
: {}),
}
})

export const themeSessionResolver = createThemeSessionResolver(sessionStorage)
export const {getSession, commitSession, destroySession} = steamSessionStorage;
Loading

0 comments on commit 9be68fd

Please sign in to comment.