Skip to main content

Authentication Overview

Tracera supports four authentication methods, all built on a unified session-based architecture. Users can link multiple providers to a single account.

Supported Methods

Google OAuth

Sign in with Google using OAuth 2.0 authorization code flow.

GitHub OAuth

Sign in with GitHub using OAuth 2.0 authorization code flow.

Steam OpenID

Sign in with Steam using OpenID 2.0. Also enables inventory access for portfolio tracking.

Magic Links

Passwordless email-based authentication. Tokens expire after 10 minutes.

How It Works

All authentication methods follow the same pattern:
1

Initiate

User clicks a login button or submits their email for a magic link.
2

Verify

The backend validates the OAuth callback or magic link token.
3

Upsert User

The user is created or found by email. The auth provider is linked to their account.
4

Create Session

A Redis-backed session is created. A secure cookie is set in the browser.
5

Redirect

The user is redirected to the frontend dashboard.

Account Linking

Users can link multiple authentication providers to a single account. The linking is email-based:
  • If a user signs in with Google ([email protected]) and later signs in with GitHub using the same email, both providers are linked to the same account.
  • Each provider can only be linked once per user (enforced by unique constraint).
  • Steam can be linked to an existing account for inventory access.

Data Model

-- Users table
CREATE TABLE users (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email       TEXT UNIQUE NOT NULL,
    name        TEXT NOT NULL DEFAULT '',
    avatar_url  TEXT NOT NULL DEFAULT '',
    role        TEXT NOT NULL DEFAULT 'user',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Auth providers (linked identities)
CREATE TABLE auth_providers (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id       UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    provider      TEXT NOT NULL,     -- 'google', 'github', 'steam', 'magic_link'
    provider_id   TEXT NOT NULL,     -- external ID from OAuth provider
    created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(provider, provider_id),   -- one account per provider identity
    UNIQUE(user_id, provider)        -- one link per provider per user
);

Frontend Integration

The frontend uses a server-side getSession() function that calls GET /api/v1/auth/me to check authentication status. This is wrapped in an AuthProvider React context for client-side access.
// Server component
const user = await getSession();

// Client component
const { user } = useAuth();
Protected pages redirect unauthenticated users to /login.