Cross-subdomain auth: shared login session across standalone apps #1

Closed
opened 2026-04-25 14:07:51 +00:00 by padreug · 1 comment
Owner

Problem

Each standalone app (main app at demo.aiolabs.dev, Castle at castle.demo.aiolabs.dev, Events at events.aiolabs.dev) has its own localStorage namespace, so users must log in separately on each one.

Currently auth stores the LNbits access token in localStorage under the key lnbits_access_token. Since localStorage is isolated per origin, there's no session sharing across subdomains.

Current Workaround

Standalone apps accept a ?token=<access_token> URL parameter. The main app can link to castle.demo.aiolabs.dev?token=xxx to relay the user's session. The token is stored in the target app's localStorage and the parameter is stripped from the URL.

This works for demo purposes but is not production-grade:

  • Token is visible in browser history and server logs
  • No token expiration/rotation
  • Requires the source app to know the user's raw token

Backend (LNbits)

  • On login (POST /auth), set an HttpOnly cookie alongside the JSON response:
    Set-Cookie: lnbits_session=<token>; Domain=.aiolabs.dev; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400
    
  • The Domain=.aiolabs.dev ensures all subdomains receive the cookie automatically

Frontend

  • Add credentials: 'include' to all fetch calls in src/lib/api/lnbits.ts
  • Keep the Bearer token header as a fallback for non-cookie environments
  • Remove localStorage token storage once cookie auth is confirmed working

Key files

  • src/lib/config/lnbits.ts — token storage helpers
  • src/lib/api/lnbits.ts — API client with auth headers
  • src/modules/base/auth/auth-service.ts — auth service login/logout flow

Considerations

  • Electron app doesn't use subdomains — keep localStorage as fallback
  • CORS config on LNbits needs to allow credentials from all app subdomains
  • Cookie expiration should match the current session timeout (24h)
  • Need to handle logout: clear cookie on all subdomains
## Problem Each standalone app (main app at `demo.aiolabs.dev`, Castle at `castle.demo.aiolabs.dev`, Events at `events.aiolabs.dev`) has its own localStorage namespace, so users must log in separately on each one. Currently auth stores the LNbits access token in `localStorage` under the key `lnbits_access_token`. Since localStorage is isolated per origin, there's no session sharing across subdomains. ## Current Workaround Standalone apps accept a `?token=<access_token>` URL parameter. The main app can link to `castle.demo.aiolabs.dev?token=xxx` to relay the user's session. The token is stored in the target app's localStorage and the parameter is stripped from the URL. This works for demo purposes but is not production-grade: - Token is visible in browser history and server logs - No token expiration/rotation - Requires the source app to know the user's raw token ## Proposed Solution: Cookie-based auth ### Backend (LNbits) - On login (`POST /auth`), set an `HttpOnly` cookie alongside the JSON response: ``` Set-Cookie: lnbits_session=<token>; Domain=.aiolabs.dev; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400 ``` - The `Domain=.aiolabs.dev` ensures all subdomains receive the cookie automatically ### Frontend - Add `credentials: 'include'` to all fetch calls in `src/lib/api/lnbits.ts` - Keep the Bearer token header as a fallback for non-cookie environments - Remove localStorage token storage once cookie auth is confirmed working ### Key files - `src/lib/config/lnbits.ts` — token storage helpers - `src/lib/api/lnbits.ts` — API client with auth headers - `src/modules/base/auth/auth-service.ts` — auth service login/logout flow ### Considerations - Electron app doesn't use subdomains — keep localStorage as fallback - CORS config on LNbits needs to allow credentials from all app subdomains - Cookie expiration should match the current session timeout (24h) - Need to handle logout: clear cookie on all subdomains
Author
Owner

Moved to aiolabs/webapp#28 with updated approaches (route-based modules as near-term solution).

Moved to aiolabs/webapp#28 with updated approaches (route-based modules as near-term solution).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/atio-home#1
No description provided.