| title | Nostr Authentication Implementation | ||||
|---|---|---|---|---|---|
| description | VisionClaw client now enforces Nostr authentication before allowing access to the application. All API requests and WebSocket connections include the user's authentication token and pubkey. | ||||
| category | how-to | ||||
| tags |
|
||||
| updated-date | 2025-12-18 | ||||
| difficulty-level | intermediate |
VisionClaw client now enforces Nostr authentication before allowing access to the application. All API requests and WebSocket connections include the user's authentication token and pubkey.
- React hook that manages authentication state
- Provides
authenticated,isLoading,user,login(),logout()functions - Automatically initializes and subscribes to auth state changes
- Handles errors gracefully
- Full-screen authentication UI shown to unauthenticated users
- Checks for NIP-07 extension (Alby, nos2x)
- Shows friendly error messages if no extension found
- Provides extension recommendations with links
- Beautiful gradient design matching app theme
- Reusable loading screen with spinner
- Used while checking auth status
- Clean, professional design
// Check auth state first
const { authenticated, isLoading, user } = useNostrAuth();
// Show loading while checking auth
if (isLoading) {
return <LoadingScreen message="Checking authentication..." />;
}
// Force login if not authenticated
if (!authenticated) {
return <NostrLoginScreen />;
}
// Sync auth state with settings store
useEffect(() => {
if (authenticated && user) {
settingsStore.setAuthenticated(true);
settingsStore.setUser({
isPowerUser: user.isPowerUser,
pubkey: user.pubkey
});
}
}, [authenticated, user]);All API endpoints now include Authorization header:
const getAuthHeaders = () => {
const token = nostrAuth.getSessionToken();
return token ? { Authorization: `Bearer ${token}` } : {};
};
// Applied to all endpoints:
axios.get('/api/settings/all', { headers: getAuthHeaders() })
axios.put('/api/settings/physics', data, { headers: getAuthHeaders() })
// ... and all other endpointsWebSocket connections now include auth:
// Include token in connection URL
const token = nostrAuth.getSessionToken();
const wsUrl = token ? `${this.url}?token=${token}` : this.url;
this.socket = new WebSocket(wsUrl);
// Send auth message after connection
const user = nostrAuth.getCurrentUser();
if (token && user) {
this.sendMessage('authenticate', {
token,
pubkey: user.pubkey
});
}- Waits for auth to be ready before initializing
- Stores authenticated state and user info
- All settings operations happen after successful auth
- User opens app → Shows LoadingScreen ("Checking authentication...")
- No auth found → Shows NostrLoginScreen
- User clicks "Login with Nostr" → Extension prompts for signature
- Auth successful → User info synced, app initializes normally
- All API calls → Include
Authorization: Bearer {token}header - WebSocket connects → Includes token in URL and sends auth message
- No NIP-07 Extension: Shows error with links to install Alby/nos2x
- Login Rejected: Shows error message, allows retry
- Session Expired: Auth service detects and shows login screen
- API 401 Errors: Will trigger logout and return to login screen
- Token stored in localStorage (nostr_session_token)
- Token included in all API requests via Authorization header
- WebSocket authenticated via URL parameter and explicit auth message
- Pubkey included with all authenticated requests
- Session verification on app startup
The backend must:
- Accept
Authorization: Bearer {token}headers on all endpoints - Verify token validity and extract pubkey
- Accept WebSocket connections with
?token=parameter - Handle
authenticateWebSocket message with{token, pubkey} - Associate all requests/connections with the authenticated pubkey
/client/src/hooks/useNostrAuth.ts- Auth hook/client/src/components/NostrLoginScreen.tsx- Login UI/client/src/components/NostrLoginScreen.css- Login styles/client/src/components/LoadingScreen.tsx- Loading UI/client/src/components/LoadingScreen.css- Loading styles
/client/src/app/App.tsx- Added auth gate/client/src/api/settingsApi.ts- Added auth headers/client/src/services/WebSocketService.ts- Added auth to WS/client/src/store/settingsStore.ts- Already had auth state (no changes needed)
To test the implementation:
- Install Alby or nos2x browser extension
- Clear localStorage to reset auth state
- Reload the app
- Should see login screen
- Click "Login with Nostr"
- Approve signature in extension
- App should load normally
- Check Network tab - all API requests have Authorization header
- Check WebSocket connection - includes token parameter
The backend needs to implement:
- Token verification middleware for REST endpoints
- WebSocket authentication handler
- User-specific settings storage (keyed by pubkey)
- Per-user filter preferences
- Session management and token expiration
- Documentation Contributing Guidelines
- Agent Control Panel User Guide
- Pipeline Operator Runbook
- Client-Side Filtering Implementation
- Physics & GPU Engine
- Authentication uses existing
nostrAuthService.tssingleton - No changes needed to
settingsStore.ts- it already had auth state variables - WebSocket will automatically reconnect with auth if connection drops
- All existing functionality preserved - just gated behind auth now