11 KiB
Svelte + Microsoft OAuth Development System
Comprehensive Methodology, Standards, and Reference Tools
PHASE 1: RESEARCH & ARCHITECTURE (IN PROGRESS)
1. Technology Stack (Monica-Enforced Standards)
Frontend Runtime: Svelte (with SvelteKit for routing/build) Backend: Bun + Hono (unchanged - proven working) Build Tool: Vite (integrated with SvelteKit) Language: TypeScript (both frontend and backend) Styling: TailwindCSS + PostCSS Storage: IndexedDB (device storage), PocketBase (token persistence) Auth: Microsoft OAuth2 directly + Service Worker credential management
PART A: MICROSOFT OAUTH2 ARCHITECTURE
A1. Overall Flow
User clicks "Sign In"
→ Service Worker intercepts Microsoft OAuth redirect URL
→ Service Worker extracts auth code
→ Backend (Hono) exchanges code for tokens using Service Worker credentials
→ Backend stores refresh token in PocketBase under user collection
→ Backend returns access token to frontend
→ Frontend stores short-lived access token in memory/sessionStorage
→ Service Worker manages token refresh on expiration
A2. Why This Approach
- Direct OAuth with Microsoft: No PocketBase auth, eliminates middle-layer complexity
- Service Worker credentials: Never expose client secret to frontend, server handles all token exchanges
- PocketBase for token storage: Persistent, synced, secure backend storage of refresh tokens
- Device storage (IndexedDB): User data (jobs, cache) stored locally without external service
- Token refresh automation: Service Worker checks token expiry and refreshes proactively
A3. Key Endpoints Needed
POST /api/auth/authorize
- Accepts: { code, state }
- Returns: { accessToken, expiresIn, user }
- Action: Exchanges OAuth code for tokens via service worker credentials
POST /api/auth/refresh
- Accepts: { }
- Returns: { accessToken, expiresIn }
- Action: Uses stored refresh token to get new access token
GET /api/auth/status
- Returns: { isAuthenticated, user, tokenExpiry }
POST /api/auth/logout
- Clears refresh token from PocketBase
A4. Microsoft Graph API Integration
- Endpoint:
https://graph.microsoft.com/v1.0/me/drive/root/children - Token required: accessToken in Authorization header
- Caching strategy: IndexedDB + device storage for file listings
- Refresh strategy: Access token → 1 hour expiry, Service Worker refreshes automatically
PART B: SVELTE ARCHITECTURE
B1. Project Structure
frontend/
src/
routes/
+page.svelte (home/jobs list)
signin/
+page.svelte (Microsoft OAuth login)
folder/
[id]/
+page.svelte (folder view)
components/
JobCard.svelte
FileListItem.svelte
SearchBar.svelte
stores/
auth.ts (writable stores for user, tokens)
jobs.ts (derived store for job data)
files.ts (derived store for file listings)
services/
oauth.ts (OAuth2 flow handler)
graph.ts (Microsoft Graph API calls)
storage.ts (IndexedDB operations)
utils/
constants.ts
app.svelte (layout wrapper)
svelte.config.js
vite.config.ts
tailwind.config.ts
B2. Svelte Stores Strategy
// auth.ts - Global auth state
export const user = writable<User | null>(null);
export const accessToken = writable<string | null>(null);
export const isAuthenticated = derived([user], ([$user]) => !!$user);
// jobs.ts - Derived from IndexedDB cache
export const jobs = writable<Job[]>([]);
export const jobsLoading = writable(false);
// files.ts - Derived from Microsoft Graph
export const files = writable<MicrosoftFile[]>([]);
export const filesLoading = writable(false);
B3. Service Worker Role
Purpose: Token refresh automation + OAuth redirect interception
Tasks:
- Listens for token expiry (via postMessage from frontend)
- Automatically calls POST /api/auth/refresh before expiry
- Updates accessToken in frontend via postMessage
- Handles Microsoft OAuth redirect URL capture (if needed)
- Persists tokens in sessionStorage (never exposed to main thread)
PART C: DEVICE STORAGE STRATEGY (IndexedDB)
C1. Database Schema
// Database: "jobinfo-app"
// Stores:
// 1. jobs
// Key: jobId (string)
// Value: { id, title, company, postedDate, ... }
// Index: "company", "postedDate"
// 2. fileCache
// Key: folderId (string)
// Value: { folderId, files: [...], lastRefreshed: timestamp }
// Index: "lastRefreshed"
// 3. userCache
// Key: "current"
// Value: { user object from Microsoft }
// 4. tokenMetadata (not storing tokens, just metadata)
// Key: "current"
// Value: { expiresAt: timestamp, refreshedAt: timestamp }
C2. IndexedDB Helper Functions
// storage.ts
export async function saveJobs(jobs: Job[]): Promise<void>
export async function getJobs(): Promise<Job[]>
export async function saveFileCache(folderId: string, files: MicrosoftFile[]): Promise<void>
export async function getFileCache(folderId: string): Promise<MicrosoftFile[] | null>
export async function clearExpiredCache(): Promise<void>
PART D: BACKEND (HONO) UPDATES
D1. New Routes
// backend/auth-routes.ts
app.post('/auth/authorize', async (c) => {
// 1. Validate request { code, state }
// 2. Exchange code for tokens using Microsoft OAuth credentials
// 3. Store refresh token in PocketBase
// 4. Return accessToken to frontend
})
app.post('/auth/refresh', async (c) => {
// 1. Get user from auth header
// 2. Retrieve refresh token from PocketBase
// 3. Exchange for new access token
// 4. Return new access token
})
app.get('/auth/status', async (c) => {
// Return current auth status
})
D2. Environment Variables Required
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_TENANT_ID=
MICROSOFT_REDIRECT_URI=http://localhost:5173/auth/callback
POCKETBASE_URL=https://pocketbase.ccllc.pro
POCKETBASE_ADMIN_EMAIL=
POCKETBASE_ADMIN_PASSWORD=
PART E: DEVELOPMENT RULES (NON-NEGOTIABLE)
RULE E1: Code Documentation Standard
Every file, every function, every store must have:
/**
* MODULE: [Name]
* PURPOSE: [What it does]
* DEPENDENCIES: [External services, stores, APIs]
* PERSISTENCE: [How/where data is stored]
* NOTES: [Any gotchas, edge cases, security considerations]
*/
RULE E2: Component Structure
Every Svelte component must have:
<!--
COMPONENT: ComponentName
PURPOSE: [What it renders]
PROPS: [Detailed prop documentation]
EVENTS: [Events this component dispatches]
STATE: [Local reactive state]
-->
<script lang="ts">
// Props with detailed comments
// Reactive declarations
// Lifecycle hooks
// Event handlers
</script>
<style>
/* TailwindCSS classes only, no custom CSS unless documented as necessary */
</style>
RULE E3: Store Pattern
Every Svelte store follows this pattern:
/**
* STORE: storeName
* PURPOSE: [What data it holds]
* UPDATES: [How and when it's updated]
* SUBSCRIPTIONS: [Components that use it]
*/
export const storeName = writable<Type>(initialValue);
// Every update must be wrapped in a clearly named function:
export async function updateStoreName(newValue: Type): Promise<void> {
// Detailed comment explaining the update
storeName.set(newValue);
}
RULE E4: API Call Pattern
Every API call to backend must follow:
/**
* API: /endpoint
* METHOD: GET/POST/etc
* HEADERS REQUIRED: [Auth, Content-Type, etc]
* REQUEST BODY: { ... with detailed types }
* RESPONSE: { ... with detailed types }
* ERRORS: [What can go wrong and how we handle it]
* RETRY STRATEGY: [Is this retried? When?]
*/
export async function apiCallName(params: Type): Promise<ResponseType> {
const token = get(accessToken);
if (!token) throw new Error('Not authenticated');
try {
const response = await fetch(`/api/endpoint`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(params),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
} catch (error) {
console.error('API call failed:', error);
throw error;
}
}
RULE E5: Error Handling Standard
No silent failures. Every promise, every fetch, every async operation:
try {
// Operation
} catch (error) {
// 1. Log error with context
console.error('Context: what were we trying to do', error);
// 2. Determine if recoverable
if (isRecoverable(error)) {
// Attempt recovery
} else {
// Notify user or escalate
}
}
RULE E6: Token Handling
- Never store tokens in localStorage (vulnerable to XSS)
- Access token: In memory only (lost on refresh, that's intentional)
- Refresh token: Only on backend in PocketBase
- Service Worker: Can read from sessionStorage only, never localStorage
- Token expiry check: Frontend checks before each API call, Service Worker proactively refreshes
RULE E7: Code Quality Standards
- Every TypeScript file has strict: true in tsconfig
- No
anytypes without explicit comment explaining why - No console.log in production code (use proper logging)
- All async operations have error handling
- All state changes are intentional and logged in stores
- No magic numbers or strings (use CONST_DEFINED_AT_TOP)
PART F: DEVELOPMENT CHECKLIST
Pre-Build Checklist
- All imports are TypeScript (.ts, not .js)
- All stores have update functions with documentation
- All API calls have error handling
- All tokens are handled per RULE E6
- All components follow component structure in RULE E2
- No hardcoded URLs (use constants)
- No console.log statements
- All environment variables are documented
Testing Checklist
- Login flow works (OAuth → token storage → API calls)
- Token refresh works (Service Worker updates token)
- File listing loads from Microsoft Graph
- File listing cached in IndexedDB
- Search/filter works from cached files
- Logout clears tokens and cache
- Offline mode works (uses cached data)
PART G: SESSION LOG LOCATION
All work logged to: /home/admin/Job-Info-Test/logs/SVELTE_SESSION_LOG.txt
Format:
[2026-01-19 10:00] Component/Store Created: ComponentName
- PURPOSE: [What it does]
- IMPLEMENTATION: [Key decisions]
- DEPENDENCIES: [What it depends on]
- STATUS: Completed / In Progress / Blocked
[2026-01-19 10:15] Fix: [Issue resolved]
- PROBLEM: [What was broken]
- DIAGNOSIS: [How we found the issue]
- SOLUTION: [What we changed]
- TESTING: [How we verified it works]
- STATUS: Verified working
NEXT STEPS
- ✅ Create this document (DONE)
- ⏳ Research Svelte + SvelteKit best practices
- ⏳ Research Microsoft OAuth2 + Service Worker patterns
- ⏳ Create backend OAuth routes
- ⏳ Create Svelte project structure
- ⏳ Create auth store and OAuth service
- ⏳ Create IndexedDB service
- ⏳ Create Microsoft Graph service
- ⏳ Build UI components
- ⏳ Integrate and test end-to-end
Status: FOUNDATION DOCUMENT CREATED - Ready for Research Phase