a a vibe-coded abomination experiment of a fragrance review platform built on the atmosphere. drydown.social

Existing Code Reference#

Related Documents:

Current Codebase (🟢 Implemented)#

Authentication Module#

File: /src/auth.ts

Key Functions:

  • getClient() - Returns singleton BrowserOAuthClient instance
  • initAuth() - Initializes OAuth and checks for existing sessions
  • login(handle) - Initiates OAuth sign-in flow with Bluesky handle

OAuth Configuration:

  • client_id: Special format using localhost with encoded redirect_uri
  • redirect_uris: ['http://127.0.0.1:5173']
  • handleResolver: 'https://bsky.social'
  • CRITICAL: Must run on 127.0.0.1:5173 (not localhost)

Example Usage:

// Initialize on app mount
useEffect(() => {
  initAuth().then(session => {
    if (session) setSession(session)
  })
}, [])

// Login
const handleLogin = async (handle: string) => {
  await login(handle) // Redirects to Bluesky OAuth
}

App Component#

File: /src/app.tsx

Responsibilities:

  • Session state management (session: OAuthSession | null)
  • Loading state during auth initialization
  • Conditional rendering: LoginForm vs authenticated UI
  • Sign out action

State:

const [session, setSession] = useState<OAuthSession | null>(null)
const [isInitializing, setIsInitializing] = useState(true)

Flow:

  1. App mounts → call initAuth()
  2. If session exists → show authenticated UI
  3. If no session → show LoginForm

LoginForm Component#

File: /src/components/LoginForm.tsx

Features:

  • Text input for Bluesky handle (placeholder: "alice.bsky.social")
  • Loading state during OAuth redirect
  • Error message display
  • Form submission handling

Example:

<form onSubmit={handleSubmit}>
  <input
    type="text"
    placeholder="alice.bsky.social"
    value={handle}
    onChange={(e) => setHandle(e.target.value)}
  />
  <button type="submit" disabled={loading}>
    {loading ? 'Signing in...' : 'Sign In'}
  </button>
  {error && <div class="error">{error}</div>}
</form>

Development Server Configuration#

File: /vite.config.ts

Key Settings:

  • Server host: 127.0.0.1 (NOT localhost)
  • Server port: 5173
  • This is REQUIRED for OAuth to work
export default defineConfig({
  plugins: [preact()],
  server: {
    host: '127.0.0.1',
    port: 5173
  }
})

Known Limitations#

Logout Implementation (/src/app.tsx):

  • Currently only clears UI state (setSession(null))
  • Does NOT revoke OAuth tokens
  • Does NOT clear browser storage
  • SDK limitation: BrowserOAuthClient doesn't expose token revocation

Future Improvement: Investigate SDK updates for proper logout


Project Structure#

/src
├── main.tsx              # App entry point
├── app.tsx               # Main App component (session mgmt)
├── auth.ts               # OAuth client singleton
├── index.css             # Global styles
├── app.css               # App-specific styles
└── components/
    └── LoginForm.tsx     # Login form component

/public
├── vite.svg              # Vite logo
└── client-metadata.json  # OAuth metadata (served statically)

index.html                # HTML entry point
vite.config.ts            # Vite config (127.0.0.1:5173)
CLAUDE.md                 # Project documentation

Related Documents: