{"_id":"@callowayisweird/steam-auth","name":"@callowayisweird/steam-auth","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@callowayisweird/steam-auth","version":"0.1.0","description":"Zero-dependency, framework-agnostic Steam OpenID 2.0 authentication. Secure by default.","license":"MIT","author":{"name":"CallowayIsWeird"},"homepage":"https://github.com/CallowayIsWeird/steam-auth#readme","bugs":{"url":"https://github.com/CallowayIsWeird/steam-auth/issues"},"repository":{"type":"git","url":"git+https://github.com/CallowayIsWeird/steam-auth.git"},"keywords":["steam","openid","auth","authentication","valve","gmod","typescript"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","lint":"tsc --noEmit","test":"vitest run","prepublishOnly":"npm run build"},"devDependencies":{"tsup":"^8.4.0","typescript":"~5.9.3","vitest":"^3.0.0"},"gitHead":"fc7987602119af8da4ed6e4672f1906bf8acaa86","_id":"@callowayisweird/steam-auth@0.1.0","_nodeVersion":"22.19.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-jycoit2KcpXTpKJSufyzeY+0zEnrlgiyu1JObVwQK2h7N9MaB42Cg8vCfxsbTDbMMcxxHNcHTuboDtXO46iNIw==","shasum":"1338fec62fbb26d02becc3d4bae514d52dd06aad","tarball":"https://registry.npmjs.org/@callowayisweird/steam-auth/-/steam-auth-0.1.0.tgz","fileCount":9,"unpackedSize":63472,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCnkUtGkJhuoK5ycpIjKZMLoVm2N7OHqeMLj/RCTv2/MwIhAKKO1ZoaMOrTmlySXDX+ItubH3AqYIq14xZ++OwA0/Bl"}]},"_npmUser":{"name":"callowayisweird","email":"henrythegamerguy62@gmail.com"},"directories":{},"maintainers":[{"name":"callowayisweird","email":"henrythegamerguy62@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/steam-auth_0.1.0_1774689990717_0.8599226366903039"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-28T09:26:30.590Z","0.1.0":"2026-03-28T09:26:30.864Z","modified":"2026-03-28T09:26:31.088Z"},"maintainers":[{"name":"callowayisweird","email":"henrythegamerguy62@gmail.com"}],"description":"Zero-dependency, framework-agnostic Steam OpenID 2.0 authentication. Secure by default.","homepage":"https://github.com/CallowayIsWeird/steam-auth#readme","keywords":["steam","openid","auth","authentication","valve","gmod","typescript"],"repository":{"type":"git","url":"git+https://github.com/CallowayIsWeird/steam-auth.git"},"author":{"name":"CallowayIsWeird"},"bugs":{"url":"https://github.com/CallowayIsWeird/steam-auth/issues"},"license":"MIT","readme":"# @callowayisweird/steam-auth\n\nZero-dependency, framework-agnostic Steam OpenID 2.0 authentication. Secure by default.\n\n## Install\n\n```bash\nnpm install @callowayisweird/steam-auth\n```\n\n## Quick Start\n\n### Hono\n\n```typescript\nimport { Hono } from \"hono\";\nimport { SteamAuth } from \"@callowayisweird/steam-auth\";\n\nconst steam = new SteamAuth({\n  realm: \"https://yoursite.com\",\n  returnUrl: \"https://yoursite.com/auth/callback\",\n});\n\nconst app = new Hono();\n\napp.get(\"/auth/login\", (c) => {\n  return c.redirect(steam.getRedirectUrl());\n});\n\napp.get(\"/auth/callback\", async (c) => {\n  const steamId = await steam.verify(new URL(c.req.url).searchParams);\n  const profile = await steam.getProfile(steamId);\n  return c.json(profile);\n});\n```\n\n### Express\n\n```typescript\nimport express from \"express\";\nimport { SteamAuth } from \"@callowayisweird/steam-auth\";\n\nconst steam = new SteamAuth({\n  realm: \"https://yoursite.com\",\n  returnUrl: \"https://yoursite.com/auth/callback\",\n});\n\nconst app = express();\n\napp.get(\"/auth/login\", (req, res) => {\n  res.redirect(steam.getRedirectUrl());\n});\n\napp.get(\"/auth/callback\", async (req, res) => {\n  const steamId = await steam.verify(req.query as Record<string, string>);\n  const profile = await steam.getProfile(steamId);\n  res.json(profile);\n});\n```\n\n### Fastify\n\n```typescript\nimport Fastify from \"fastify\";\nimport { SteamAuth } from \"@callowayisweird/steam-auth\";\n\nconst steam = new SteamAuth({\n  realm: \"https://yoursite.com\",\n  returnUrl: \"https://yoursite.com/auth/callback\",\n});\n\nconst app = Fastify();\n\napp.get(\"/auth/login\", async (req, reply) => {\n  return reply.redirect(steam.getRedirectUrl());\n});\n\napp.get(\"/auth/callback\", async (req, reply) => {\n  const steamId = await steam.verify(req.query as Record<string, string>);\n  const profile = await steam.getProfile(steamId);\n  return profile;\n});\n```\n\n## Security\n\nThis library fixes the **passport-steam authentication bypass vulnerability** and performs 6 security checks on every callback:\n\n1. **Mode validation** -- Ensures `openid.mode` is `id_res`\n2. **Return URL verification** -- Validates `openid.return_to` matches your configured `returnUrl` exactly. This is the check that passport-steam skipped, allowing attackers to forge authentication responses.\n3. **Endpoint validation** -- Confirms `openid.op_endpoint` is the real Steam endpoint (`https://steamcommunity.com/openid/login`)\n4. **Claimed ID format check** -- Validates `openid.claimed_id` matches the expected Steam URL pattern\n5. **Replay protection** -- Tracks nonces to prevent replay attacks. Nonces are automatically cleaned up after 5 minutes.\n6. **Server-side verification** -- POSTs back to Steam's `check_authentication` endpoint to confirm the assertion is genuine\n\n## API Reference\n\n### `new SteamAuth(options)`\n\n| Option      | Type       | Description                                       |\n| ----------- | ---------- | ------------------------------------------------- |\n| `realm`     | `string`   | Your site URL, e.g. `\"https://yoursite.com\"`      |\n| `returnUrl` | `string`   | Callback URL, e.g. `\"https://yoursite.com/auth/callback\"` |\n| `fetch`     | `function` | Optional custom fetch implementation for testing  |\n\n### `steam.getRedirectUrl(): string`\n\nReturns the Steam OpenID login URL. Redirect the user here.\n\n### `steam.verify(query): Promise<string>`\n\nVerifies the OpenID callback and returns the user's SteamID64. Accepts either a `Record<string, string>` or `URLSearchParams`.\n\nThrows one of the typed errors below on failure.\n\n### `steam.getProfile(steamId): Promise<SteamProfile>`\n\nFetches the user's public Steam profile (no API key required). Returns:\n\n```typescript\ninterface SteamProfile {\n  steamId: string;\n  name: string;\n  avatarUrl: string;      // 64x64\n  avatarMedium: string;   // 184x184\n  avatarFull: string;     // Full size\n  profileUrl: string;\n  personaState: number;   // 0=offline, 1=online, etc.\n}\n```\n\n### `steam.destroy(): void`\n\nStops the nonce cleanup interval and clears stored nonces. Call this when shutting down.\n\n## Error Handling\n\nAll errors extend `SteamAuthError` and have a `code` property for programmatic handling:\n\n```typescript\nimport {\n  SteamAuth,\n  VerificationError,\n  ReturnUrlMismatchError,\n  SteamUnavailableError,\n} from \"@callowayisweird/steam-auth\";\n\ntry {\n  const steamId = await steam.verify(query);\n} catch (err) {\n  if (err instanceof ReturnUrlMismatchError) {\n    // Possible attack -- return_to was tampered with\n  } else if (err instanceof SteamUnavailableError) {\n    // Steam is down, retry later\n  } else if (err instanceof VerificationError) {\n    // Verification failed\n  }\n}\n```\n\n| Error Class              | Code                   | Description                                |\n| ------------------------ | ---------------------- | ------------------------------------------ |\n| `SteamAuthError`         | *(varies)*             | Base class for all errors                  |\n| `VerificationError`      | `VERIFICATION_FAILED`  | Steam's check_authentication returned invalid |\n| `ReturnUrlMismatchError` | `RETURN_URL_MISMATCH`  | return_to doesn't match configured returnUrl |\n| `InvalidClaimedIdError`  | `INVALID_CLAIMED_ID`   | claimed_id doesn't match Steam format      |\n| `InvalidEndpointError`   | `INVALID_ENDPOINT`     | op_endpoint isn't the real Steam endpoint  |\n| `ReplayAttackError`      | `REPLAY_ATTACK`        | Nonce was already used                     |\n| `SteamUnavailableError`  | `STEAM_UNAVAILABLE`    | Steam didn't respond or returned non-200   |\n| `ProfileFetchError`      | `PROFILE_FETCH_FAILED` | Failed to fetch Steam profile              |\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-16e9fe8b2b218ceb20b7bbad45a14398"}