Remix Auth Steam Strategy
remix-auth-steam provides a specialized authentication strategy for integrating Steam OpenID into Remix applications via the `remix-auth` library. Currently at version 1.0.4, this package acts as a bridge, enabling developers to leverage Steam's authentication flow within their Remix projects on Node.js runtimes. While it doesn't specify a strict release cadence, updates appear to be made on demand to address bug fixes, as seen in recent patch versions. Its primary differentiator is its focused implementation for Steam, building upon the extensible architecture of `remix-auth`. Developers should be aware of its specific dependency on `@remix-run/server-runtime` and `remix-auth` itself, and ensure their Remix environment is configured for server-side operations, as browser or edge worker support for Steam OpenID directly is not fully tested or guaranteed by this package. It ships with TypeScript types for improved developer experience.
Common errors
-
Error: Authenticate method missing 'strategy' argument.
cause The `authenticator.authenticate` method was called without specifying the strategy name (e.g., 'steam').fixEnsure you pass the strategy name as the first argument: `authenticator.authenticate('steam', request, { ... })`. -
OpenID authentication failed: Missing realm parameter.
cause Steam OpenID requires a `realm` parameter, which is typically derived from your `returnURL`. Older versions or misconfigurations might lead to this error.fixUpdate to at least `remix-auth-steam@1.0.4` which fixed realm passing. Ensure your `returnURL` is correctly formatted and accessible from Steam. -
ReferenceError: authenticator is not defined
cause The `authenticator` instance was not correctly imported or instantiated in the server-side module where it's being used (e.g., `app/routes/auth/steam.tsx`).fixVerify that `authenticator` is properly initialized in `app/services/auth.server.ts` and then correctly imported into your routes: `import { authenticator } from '~/services/auth.server';`. -
Error: Could not retrieve Steam API Key. Please provide one.
cause The `apiKey` option was either omitted or provided with an invalid or empty string during `SteamStrategy` instantiation.fixProvide a valid Steam API key obtained from `https://steamcommunity.com/dev/apikey` as the `apiKey` option to the `SteamStrategy` constructor, preferably via environment variables: `apiKey: process.env.STEAM_API_KEY`.
Warnings
- gotcha Sensitive configuration values such as `apiKey` and `secrets` for session storage must be loaded from environment variables (e.g., `process.env.STEAM_API_KEY`). Hardcoding these values is a security risk, especially in production environments.
- gotcha The `returnURL` provided in the `SteamStrategy` configuration must precisely match the callback URL configured in your Steam API key settings and the actual route in your Remix application. Mismatches will lead to authentication failures.
- gotcha The library explicitly states that Cloudflare runtime support is 'Not tested'. While Remix itself can run on Cloudflare Pages/Workers, specific adaptations for Steam OpenID or underlying dependencies might be required, and functionality is not guaranteed out-of-the-box.
- gotcha As a relatively new library (initial release v1.0.1), while recent updates have focused on bug fixes, future minor versions of `remix-auth-steam` may introduce breaking API changes as it matures.
- gotcha This package is a strategy for `remix-auth`. Breaking changes or significant API shifts in `remix-auth` itself or `@remix-run/node` (a peer dependency) could require updates or modifications to how `remix-auth-steam` is integrated and used.
Install
-
npm install remix-auth-steam -
yarn add remix-auth-steam -
pnpm add remix-auth-steam
Imports
- SteamStrategy
const SteamStrategy = require('remix-auth-steam').SteamStrategy;import { SteamStrategy } from 'remix-auth-steam'; - SteamStrategyVerifyParams
import { SteamStrategyVerifyParams } from 'remix-auth-steam';import type { SteamStrategyVerifyParams } from 'remix-auth-steam'; - authenticator
import { authenticator } from '~/services/auth.server';
Quickstart
import { createCookieSessionStorage, redirect } from "@remix-run/node";
import type { LoaderFunction, ActionFunction } from "@remix-run/node";
import { Authenticator } from "remix-auth";
import { SteamStrategy, SteamStrategyVerifyParams } from "remix-auth-steam";
import { useLoaderData, Form, Link } from "@remix-run/react";
import React from "react";
// app/services/session.server.ts
// Helper to calculate cookie expiration
const calculateExpirationDate = (days: number) => {
const expDate = new Date();
expDate.setDate(expDate.getDate() + days);
return expDate;
};
// Session storage setup
export let sessionStorage = createCookieSessionStorage({
cookie: {
name: "_session",
sameSite: "lax",
path: "/",
httpOnly: true,
secrets: [process.env.SESSION_SECRET ?? "super-secret-dev-key"], // IMPORTANT: Use a strong, production secret from environment variables
secure: process.env.NODE_ENV === "production",
expires: calculateExpirationDate(7),
},
});
export let { getSession, commitSession, destroySession } = sessionStorage;
// app/services/auth.server.ts
// Define the User type based on SteamStrategyVerifyParams
export type User = SteamStrategyVerifyParams;
// Create an Authenticator instance
export let authenticator = new Authenticator<User>(sessionStorage);
// Register the SteamStrategy
authenticator.use(
new SteamStrategy(
{
returnURL: "http://localhost:3000/auth/steam/callback",
apiKey: process.env.STEAM_API_KEY ?? "YOUR_STEAM_API_KEY", // IMPORTANT: Get your API key from https://steamcommunity.com/dev/apikey
},
// The verify callback: here you can perform additional checks or database operations
async (user) => {
// For this example, we simply return the user data provided by Steam
console.log("Steam User Authenticated:", user.nickname, user.steamid);
return user;
}
),
"steam" // The name of the strategy to be used in authenticate calls
);
// app/routes/auth/steam.tsx
// This route initiates the Steam authentication flow
export let loader: LoaderFunction = async ({ request }) => {
return authenticator.authenticate("steam", request);
};
// app/routes/auth/steam/callback.tsx
// This route handles the callback from Steam after authentication
export let loader: LoaderFunction = ({ request }) => {
return authenticator.authenticate("steam", request, {
successRedirect: "/", // Redirect to home on success
failureRedirect: "/login", // Redirect to login on failure
});
};
// app/routes/login.tsx
export default function Login() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<h1>Login</h1>
<p>You need to log in to access this page.</p>
<Link to="/auth/steam">
<button>Login with Steam</button>
</Link>
</div>
);
}
// app/routes/index.tsx
export let loader: LoaderFunction = async ({ request }) => {
// Check if the user is authenticated
const user = await authenticator.isAuthenticated(request);
return user;
};
export default function Index() {
const user = useLoaderData<User | null>();
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
{user ? (
<>
<h1>Welcome, {user.nickname}!</h1>
<p>Your SteamID: {user.steamid}</p>
<Form action="/logout" method="post">
<button type="submit">Logout</button>
</Form>
</>
) : (
<>
<h1>Not Authenticated</h1>
<p>
<Link to="/login">Login with Steam</Link>
</p>
</>
)}
</div>
);
}
// app/routes/logout.tsx
export let action: ActionFunction = async ({ request }) => {
// Destroy the session and redirect to the login page
await authenticator.logout(request, { redirectTo: "/login" });
return redirect("/login");
};