diff --git a/.kiro/specs/user-auth-and-sharing/tasks.md b/.kiro/specs/user-auth-and-sharing/tasks.md index 400d6cf..a49f0b8 100644 --- a/.kiro/specs/user-auth-and-sharing/tasks.md +++ b/.kiro/specs/user-auth-and-sharing/tasks.md @@ -264,7 +264,7 @@ - [ ] 18. Checkpoint - Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. -- [ ] 19. Update PWA service worker for authentication +- [x] 19. Update PWA service worker for authentication - Handle authentication errors in offline mode - Cache authentication-related pages appropriately - Update manifest if needed diff --git a/PWA_AUTH_GUIDE.md b/PWA_AUTH_GUIDE.md new file mode 100644 index 0000000..4e24b5f --- /dev/null +++ b/PWA_AUTH_GUIDE.md @@ -0,0 +1,189 @@ +# PWA Authentication Guide + +## Overview + +The Ultimate Lists App PWA has been updated to handle authentication properly in offline scenarios. This guide explains how the service worker manages authentication-related requests and what users can expect when offline. + +## Service Worker Updates + +### Cache Version + +The service worker cache has been updated to version 2 (`ultimate-lists-v2`) to ensure proper cache invalidation and updates. + +### Authentication Pages Caching + +The following authentication-related pages are now cached during service worker installation: + +- `/auth/login` - Login page +- `/auth/register` - Registration page +- `/auth/error` - Authentication error page + +This allows users to view these pages even when offline, though actual authentication requires an internet connection. + +### Authentication Request Handling + +#### API Authentication Requests (`/api/auth/*`) + +Authentication API calls (login, register, session validation) always require a network connection. When offline: + +- The service worker returns a 503 Service Unavailable response +- A JSON error message explains that authentication requires internet connectivity +- The error message: "Authentication requires an internet connection. Please check your connection and try again." + +#### Authentication Pages (`/auth/*`) + +Authentication pages use a cache-first strategy with network fallback: + +1. Try to fetch the latest version from the network +2. Cache successful responses for offline access +3. If offline, serve the cached version +4. If no cached version exists, redirect to the offline page + +### Authentication Error Handling + +The service worker now properly handles authentication errors (401, 403): + +- Authentication errors are **not cached** +- This prevents stale authentication states from being served +- Users always get fresh authentication status from the server + +### Offline Page Enhancements + +The offline page (`/app/offline/page.tsx`) has been enhanced with: + +1. **Online Detection**: Automatically detects when the connection is restored +2. **Auto-redirect**: Redirects to the home page when back online +3. **Authentication Notice**: Displays a clear message that login/registration requires internet +4. **Visual Feedback**: Shows different states for offline vs. back online + +## User Experience + +### When Online + +- All authentication features work normally +- Login, registration, and session management function as expected +- Pages are cached for faster subsequent loads + +### When Offline + +#### Authenticated Users + +- Can view cached lists and tasks +- Can make changes that will sync when back online +- Session remains valid (until expiration) +- Cannot perform new authentication actions + +#### Unauthenticated Users + +- Can view cached authentication pages (login/register forms) +- Cannot actually log in or register (requires internet) +- See clear error messages explaining the need for connectivity +- Are directed to the offline page with authentication guidance + +## Testing Offline Behavior + +### Manual Testing + +1. **Test Offline Login Attempt**: + - Open the app while online + - Navigate to `/auth/login` + - Disconnect from the internet (or use DevTools offline mode) + - Try to submit login credentials + - Verify error message appears + +2. **Test Cached Authentication Pages**: + - Visit `/auth/login` while online + - Disconnect from the internet + - Refresh the page + - Verify the login page loads from cache + +3. **Test Online Detection**: + - Navigate to `/offline` while offline + - Reconnect to the internet + - Verify automatic redirect to home page + +### Chrome DevTools Testing + +1. Open Chrome DevTools (F12) +2. Go to Application tab → Service Workers +3. Check "Offline" to simulate offline mode +4. Test authentication flows +5. Verify cache contents in Application → Cache Storage + +## Technical Implementation + +### Service Worker Strategy + +```javascript +// Authentication API calls - Network only +if (url.pathname.startsWith('/api/auth/')) { + // Always try network, return 503 if offline +} + +// Authentication pages - Cache-first with network fallback +if (url.pathname.startsWith('/auth/')) { + // Try network, fall back to cache, then offline page +} +``` + +### Error Response Format + +When authentication API calls fail offline: + +```json +{ + "error": "Authentication requires an internet connection. Please check your connection and try again." +} +``` + +### Cache Strategy Summary + +| Resource Type | Strategy | Offline Behavior | +|--------------|----------|------------------| +| Authentication API | Network only | Return 503 error | +| Authentication Pages | Cache-first | Serve cached version | +| Static Assets | Cache-first | Serve cached version | +| API Responses | Stale-while-revalidate | Serve cached, update in background | +| Regular Pages | Stale-while-revalidate | Serve cached or offline page | + +## Security Considerations + +1. **No Credential Caching**: User credentials are never cached by the service worker +2. **Session Validation**: Sessions are validated server-side on each request +3. **Error State Handling**: Authentication errors (401, 403) are not cached +4. **Secure Token Storage**: Session tokens are managed by NextAuth.js, not the service worker + +## Future Enhancements + +Potential improvements for authentication in offline scenarios: + +- Queue authentication attempts for when connection is restored +- Show estimated time until session expiration +- Provide offline-specific UI hints for authenticated users +- Implement background sync for authentication state updates + +## Troubleshooting + +### Service Worker Not Updating + +If changes aren't reflected: + +1. Unregister the old service worker in DevTools +2. Clear cache storage +3. Hard refresh the page (Ctrl+Shift+R) + +### Authentication Not Working Offline + +This is expected behavior. Authentication requires: + +- Active internet connection +- Server communication for credential validation +- Session token generation and validation + +### Cached Pages Showing Old Content + +The service worker updates cached pages when online. To force an update: + +1. Ensure you're online +2. The service worker checks for updates every minute +3. Or manually update in DevTools → Application → Service Workers → Update diff --git a/app/offline/page.tsx b/app/offline/page.tsx index 3ee02f4..5a4948f 100644 --- a/app/offline/page.tsx +++ b/app/offline/page.tsx @@ -1,8 +1,30 @@ 'use client'; import { WifiOff } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; export default function OfflinePage() { + const [isOnline, setIsOnline] = useState(false); + const router = useRouter(); + + useEffect(() => { + // Check if we're back online + const handleOnline = () => { + setIsOnline(true); + // Automatically redirect after a short delay + setTimeout(() => { + router.push('/'); + }, 1000); + }; + + window.addEventListener('online', handleOnline); + + return () => { + window.removeEventListener('online', handleOnline); + }; + }, [router]); + return (
@@ -13,36 +35,52 @@ export default function OfflinePage() {

- You're Offline + {isOnline ? "You're Back Online!" : "You're Offline"}

-

- It looks like you've lost your internet connection. Don't worry, your data is safe and will sync when you're back online. -

+ {isOnline ? ( +

+ Your connection has been restored. Redirecting... +

+ ) : ( + <> +

+ It looks like you've lost your internet connection. Don't worry, your data is safe and will sync when you're back online. +

-
-

What you can do:

- -
+
+

What you can do:

+ +
+ +
+

Note about authentication:

+

+ Login and registration require an internet connection. If you need to sign in, please connect to the internet first. +

+
+ + )}
diff --git a/public/sw.js b/public/sw.js index dc1c724..f796e91 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,13 +1,16 @@ // Service Worker for Ultimate Lists App // Implements caching strategies for offline functionality -const CACHE_NAME = 'ultimate-lists-v1'; -const RUNTIME_CACHE = 'ultimate-lists-runtime-v1'; +const CACHE_NAME = 'ultimate-lists-v2'; +const RUNTIME_CACHE = 'ultimate-lists-runtime-v2'; // Static assets to cache on install const STATIC_ASSETS = [ '/', '/offline', + '/auth/login', + '/auth/register', + '/auth/error', '/manifest.json', '/icon-192.svg', '/icon-512.svg', @@ -58,6 +61,12 @@ self.addEventListener('fetch', (event) => { return; } + // Handle authentication-related requests + if (isAuthRequest(url)) { + event.respondWith(handleAuthRequest(request)); + return; + } + // Cache-first strategy for static assets if (isStaticAsset(url)) { event.respondWith(cacheFirst(request)); @@ -90,6 +99,58 @@ function isPageRequest(url) { return url.origin === self.location.origin && !url.pathname.includes('.'); } +// Helper: Check if request is authentication-related +function isAuthRequest(url) { + return url.pathname.startsWith('/auth/') || + url.pathname.startsWith('/api/auth/'); +} + +// Strategy: Handle authentication requests +async function handleAuthRequest(request) { + const url = new URL(request.url); + + // For authentication API calls, always try network first + if (url.pathname.startsWith('/api/auth/')) { + try { + const response = await fetch(request); + return response; + } catch (error) { + console.error('Authentication API request failed (offline):', error); + // Return a JSON error response for API calls + return new Response( + JSON.stringify({ + error: 'Authentication requires an internet connection. Please check your connection and try again.' + }), + { + status: 503, + statusText: 'Service Unavailable', + headers: { 'Content-Type': 'application/json' } + } + ); + } + } + + // For authentication pages, use cache-first with network fallback + const cache = await caches.open(CACHE_NAME); + const cached = await cache.match(request); + + try { + const response = await fetch(request); + if (response.ok) { + cache.put(request, response.clone()); + } + return response; + } catch (error) { + console.error('Authentication page request failed:', error); + // Return cached version if available + if (cached) { + return cached; + } + // Otherwise redirect to offline page + return caches.match('/offline'); + } +} + // Strategy: Cache-first (for static assets) async function cacheFirst(request) { const cache = await caches.open(CACHE_NAME); @@ -117,6 +178,12 @@ async function staleWhileRevalidate(request) { const cached = await cache.match(request); const fetchPromise = fetch(request).then((response) => { + // Handle authentication errors (401, 403) + if (response.status === 401 || response.status === 403) { + // Don't cache authentication errors + return response; + } + if (response.ok) { cache.put(request, response.clone()); } @@ -141,6 +208,13 @@ async function staleWhileRevalidate(request) { async function networkFirst(request) { try { const response = await fetch(request); + + // Handle authentication errors (401, 403) + if (response.status === 401 || response.status === 403) { + // Don't cache authentication errors + return response; + } + if (response.ok) { const cache = await caches.open(RUNTIME_CACHE); cache.put(request, response.clone());