feat: add Steam OpenID login
All checks were successful
Build & Deploy / build (push) Successful in 47s
Build & Deploy / deploy (push) Successful in 4s
Build & Deploy / bump-version (push) Successful in 2s

- Add Steam OpenID 2.0 authentication routes (login + callback)
- Enable Steam button in LoginModal (was placeholder)
- Unified user ID system: getUserId() supports Discord, Steam, Admin
- Update soundboard user-sound endpoints for Steam users
- UserSettings now works for both Discord and Steam providers
- Steam hover uses brand color #66c0f4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Daniel 2026-03-10 22:18:37 +01:00
parent aa998c9b44
commit d135aab6dc
6 changed files with 162 additions and 38 deletions

View file

@ -17,8 +17,9 @@ interface PluginInfo {
interface AuthUser {
authenticated: boolean;
provider?: 'discord' | 'admin';
provider?: 'discord' | 'steam' | 'admin';
discordId?: string;
steamId?: string;
username?: string;
avatar?: string | null;
globalName?: string | null;
@ -69,6 +70,8 @@ export default function App() {
// Derived state
const isAdmin = user.authenticated && (user.provider === 'admin' || user.isAdmin === true);
const isDiscordUser = user.authenticated && user.provider === 'discord';
const isSteamUser = user.authenticated && user.provider === 'steam';
const isRegularUser = isDiscordUser || isSteamUser;
// Electron auto-update state
const isElectron = !!(window as any).electronAPI?.isElectron;
@ -239,7 +242,7 @@ export default function App() {
setShowLoginModal(true);
} else if (isAdmin) {
setShowAdminPanel(true);
} else if (isDiscordUser) {
} else if (isRegularUser) {
setShowUserSettings(true);
}
}
@ -294,7 +297,7 @@ export default function App() {
}
>
{user.authenticated ? (
isDiscordUser && user.avatar ? (
isRegularUser && user.avatar ? (
<img src={user.avatar} alt="" className="hub-user-avatar" />
) : (
<span className="hub-user-icon">{isAdmin ? '\uD83D\uDD27' : '\uD83D\uDC64'}</span>
@ -435,11 +438,12 @@ export default function App() {
/>
)}
{/* User Settings (Discord users) */}
{showUserSettings && isDiscordUser && user.discordId && (
{/* User Settings (Discord + Steam users) */}
{showUserSettings && isRegularUser && (
<UserSettings
user={{
discordId: user.discordId,
id: user.discordId || user.steamId || '',
provider: user.provider as 'discord' | 'steam',
username: user.username ?? '',
avatar: user.avatar ?? null,
globalName: user.globalName ?? null,

View file

@ -63,14 +63,15 @@ export default function LoginModal({ onClose, onAdminLogin, providers }: LoginMo
</a>
)}
{/* Steam — placeholder */}
<button className="hub-login-provider-btn steam" disabled title="Bald verfügbar">
<svg className="hub-login-provider-icon" viewBox="0 0 24 24" fill="currentColor" width="22" height="22">
<path d="M11.979 0C5.678 0 .511 4.86.022 11.037l6.432 2.658c.545-.371 1.203-.59 1.912-.59.063 0 .125.004.188.006l2.861-4.142V8.91c0-2.495 2.028-4.524 4.524-4.524 2.494 0 4.524 2.031 4.524 4.527s-2.03 4.525-4.524 4.525h-.105l-4.076 2.911c0 .052.004.105.004.159 0 1.875-1.515 3.396-3.39 3.396-1.635 0-3.016-1.173-3.331-2.727L.436 15.27C1.862 20.307 6.486 24 11.979 24c6.627 0 12.001-5.373 12.001-12S18.606 0 11.979 0zM7.54 18.21l-1.473-.61c.262.543.714.999 1.314 1.25 1.297.539 2.793-.076 3.332-1.375.263-.63.264-1.319.005-1.949s-.75-1.121-1.377-1.383c-.624-.26-1.29-.249-1.878-.03l1.523.63c.956.4 1.409 1.5 1.009 2.455-.397.957-1.497 1.41-2.454 1.012H7.54zm11.415-9.303a3.015 3.015 0 0 0-3.016-3.016 3.015 3.015 0 0 0-3.016 3.016 3.015 3.015 0 0 0 3.016 3.016 3.015 3.015 0 0 0 3.016-3.016zm-5.273-.005c0-1.248 1.013-2.26 2.26-2.26 1.246 0 2.26 1.013 2.26 2.26 0 1.247-1.014 2.26-2.26 2.26-1.248 0-2.26-1.013-2.26-2.26z" />
</svg>
<span>Steam Login</span>
<span className="hub-login-soon">bald</span>
</button>
{/* Steam */}
{providers.steam && (
<a href="/api/auth/steam" className="hub-login-provider-btn steam">
<svg className="hub-login-provider-icon" viewBox="0 0 24 24" fill="currentColor" width="22" height="22">
<path d="M11.979 0C5.678 0 .511 4.86.022 11.037l6.432 2.658c.545-.371 1.203-.59 1.912-.59.063 0 .125.004.188.006l2.861-4.142V8.91c0-2.495 2.028-4.524 4.524-4.524 2.494 0 4.524 2.031 4.524 4.527s-2.03 4.525-4.524 4.525h-.105l-4.076 2.911c0 .052.004.105.004.159 0 1.875-1.515 3.396-3.39 3.396-1.635 0-3.016-1.173-3.331-2.727L.436 15.27C1.862 20.307 6.486 24 11.979 24c6.627 0 12.001-5.373 12.001-12S18.606 0 11.979 0zM7.54 18.21l-1.473-.61c.262.543.714.999 1.314 1.25 1.297.539 2.793-.076 3.332-1.375.263-.63.264-1.319.005-1.949s-.75-1.121-1.377-1.383c-.624-.26-1.29-.249-1.878-.03l1.523.63c.956.4 1.409 1.5 1.009 2.455-.397.957-1.497 1.41-2.454 1.012H7.54zm11.415-9.303a3.015 3.015 0 0 0-3.016-3.016 3.015 3.015 0 0 0-3.016 3.016 3.015 3.015 0 0 0 3.016 3.016 3.015 3.015 0 0 0 3.016-3.016zm-5.273-.005c0-1.248 1.013-2.26 2.26-2.26 1.246 0 2.26 1.013 2.26 2.26 0 1.247-1.014 2.26-2.26 2.26-1.248 0-2.26-1.013-2.26-2.26z" />
</svg>
<span>Mit Steam anmelden</span>
</a>
)}
{/* Admin */}
{providers.admin && (

View file

@ -1,7 +1,8 @@
import { useState, useEffect, useCallback } from 'react';
interface UserInfo {
discordId: string;
id: string;
provider: 'discord' | 'steam';
username: string;
avatar: string | null;
globalName: string | null;
@ -133,7 +134,9 @@ export default function UserSettings({ user, onClose, onLogout }: UserSettingsPr
)}
<div className="hub-usettings-user-info">
<span className="hub-usettings-username">{user.globalName || user.username}</span>
<span className="hub-usettings-discriminator">@{user.username}</span>
<span className="hub-usettings-discriminator">
{user.provider === 'steam' ? 'Steam' : `@${user.username}`}
</span>
</div>
</div>
<div className="hub-usettings-header-actions">

View file

@ -2456,9 +2456,9 @@ html, body {
border-color: #5865F2;
background: rgba(88, 101, 242, 0.08);
}
.hub-login-provider-btn.steam:hover:not(:disabled) {
border-color: #1b2838;
background: rgba(27, 40, 56, 0.15);
.hub-login-provider-btn.steam:hover {
border-color: #66c0f4;
background: rgba(102, 192, 244, 0.08);
}
.hub-login-provider-btn.admin:hover {
border-color: var(--accent);