11 KiB
================================================================================ QUICK START: Integrating Unified Auth into NewApproach
This guide shows how to integrate the auth system into your NewApproach project.
================================================================================ STEP 1: SETUP ENVIRONMENT VARIABLES
Create or update .env file in NewApproach root:
# PocketBase
POCKETBASE_URL=http://localhost:8090
POCKETBASE_SERVICE_EMAIL=service@example.com
POCKETBASE_SERVICE_PASSWORD=your_service_password
# Microsoft Graph (Service Principal)
GRAPH_TENANT_ID=your-tenant-id
GRAPH_CLIENT_ID=your-client-id
GRAPH_CLIENT_SECRET=your-client-secret
Note: .env should NOT be committed to git. Add to .gitignore.
================================================================================ STEP 2: UPDATE FRONTEND (public/index.html)
Current state: public/index.html loads static app
Required changes:
- Add PocketBase SDK
- Add auth module
- Initialize auth on page load
- Use tokens for API calls
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NewApproach</title>
<link rel="stylesheet" href="/output.css">
<!-- PocketBase SDK (REQUIRED for auth) -->
<script src="https://cdn.jsdelivr.net/npm/pocketbase@0.26.5/dist/pocketbase.umd.js"></script>
</head>
<body class="bg-gray-50">
<div id="app"></div>
<script type="module">
// Import auth module
import Auth from '/auth.unified.ts';
// Initialize auth on page load
async function init() {
try {
// Initialize auth with both PocketBase and Graph patterns
await Auth.init('pb+graph', {
pbUrl: 'http://localhost:8090'
});
// Get tokens (this shows login popup if needed)
const pbUserToken = await Auth.getToken('pb-user');
const graphToken = await Auth.getToken('graph-user');
// Get user info
const { displayName, email } = Auth.getUserInfo();
console.log(`Logged in as: ${displayName} (${email})`);
// Now load the app
await loadApp(pbUserToken, graphToken);
} catch (err) {
console.error('Auth initialization failed:', err);
document.getElementById('app').innerHTML = '<p style="color: red;">Authentication failed. Please refresh.</p>';
}
}
async function loadApp(pbToken, graphToken) {
const app = document.getElementById('app');
app.innerHTML = `
<div class="min-h-screen flex flex-col items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
<div class="bg-white rounded-lg shadow-lg p-8 max-w-md w-full">
<h1 class="text-3xl font-bold text-gray-800 mb-2">NewApproach</h1>
<p class="text-gray-600 mb-6">Full-stack TypeScript + Hono + TailwindCSS</p>
<div class="mb-4 p-4 bg-gray-50 rounded">
<p class="text-sm text-gray-600"><strong>Status:</strong> Authenticated</p>
<p class="text-sm text-gray-600"><strong>PB Token:</strong> ${pbToken.substring(0, 20)}...</p>
<p class="text-sm text-gray-600"><strong>Graph Token:</strong> ${graphToken ? graphToken.substring(0, 20) + '...' : 'Not available'}</p>
</div>
<button
id="fetchBtn"
class="w-full bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded transition"
>
Fetch API Info
</button>
<button
id="logoutBtn"
class="w-full mt-2 bg-gray-500 hover:bg-gray-600 text-white font-semibold py-2 px-4 rounded transition"
>
Logout
</button>
<div id="result" class="mt-6 p-4 bg-gray-50 rounded hidden">
<pre id="resultContent" class="text-sm text-gray-700 whitespace-pre-wrap"></pre>
</div>
</div>
</div>
`;
// Fetch with token
document.getElementById('fetchBtn').addEventListener('click', async () => {
try {
const response = await fetch('/api/info', {
headers: {
'Authorization': `Bearer ${pbToken}`,
'X-Graph-Token': graphToken
}
});
const data = await response.json();
document.getElementById('result').classList.remove('hidden');
document.getElementById('resultContent').textContent = JSON.stringify(data, null, 2);
} catch (err) {
alert('API call failed: ' + err.message);
}
});
// Logout
document.getElementById('logoutBtn').addEventListener('click', () => {
Auth.clearAllTokens();
location.reload();
});
}
// Start
init();
</script>
<script src="/app.js"></script>
</body>
</html>
================================================================================ STEP 3: UPDATE BACKEND (src/backend/server.ts)
Current state: Basic Hono server with /api/health and /api/info
Required changes:
- Import auth module
- Initialize backend auth
- Add service token endpoints
- Use tokens in routes
import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';
import backendAuth, { serviceTokenMiddleware, createTokenEndpoint, createRefreshEndpoint } from './auth';
const app = new Hono();
// Initialize backend auth (loads from env vars)
backendAuth.init({
pbUrl: process.env.POCKETBASE_URL
});
// Middleware: Inject service tokens into context
app.use('/*', serviceTokenMiddleware());
// Static files
app.use('/*', serveStatic({ root: './public' }));
// Health check
app.get('/api/health', (c) => {
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Info endpoint (uses tokens)
app.get('/api/info', (c) => {
const pbToken = c.get('pbServiceToken');
const graphToken = c.get('graphServiceToken');
return c.json({
name: 'NewApproach',
version: '0.1.0',
description: 'Full-stack TypeScript + Hono + TailwindCSS',
authStatus: {
pbServiceToken: !!pbToken,
graphServiceToken: !!graphToken
}
});
});
// Example: Use service tokens to call external APIs
app.get('/api/graph-data', async (c) => {
try {
const graphToken = await backendAuth.getGraphServicePrincipalToken();
// Call Microsoft Graph API
const response = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: { 'Authorization': `Bearer ${graphToken}` }
});
const data = await response.json();
return c.json({ success: true, data });
} catch (err) {
return c.json({ success: false, error: (err as Error).message }, 500);
}
});
// Register token endpoints
createTokenEndpoint(app);
createRefreshEndpoint(app);
// SPA fallback
app.get('*', serveStatic({ path: './public/index.html', root: './public' }));
// Start server
const port = process.env.PORT || 3000;
export default {
port,
fetch: app.fetch,
};
console.log(`🚀 Server running on http://localhost:${port}`);
================================================================================ STEP 4: COMPILE & RUN
cd /home/admin/Job-Info/NewApproach
# Install dependencies (if needed)
bun install
# Compile TypeScript
bun run typecheck
# Start dev server (watches for changes)
bun run dev
Server starts on http://localhost:3000
================================================================================ STEP 5: TEST
- Open http://localhost:3000 in browser
- Should see "Sign in with Microsoft" button
- Click to authenticate via PocketBase OAuth
- After login, you should see:
- User info (if available)
- Tokens in localStorage
- "Fetch API Info" button working
- Logout button clearing tokens
Check browser console for logs:
- [Auth] Initialized with pattern...
- [Auth] PocketBase user token acquired via OAuth
- [Auth] Token stored...
================================================================================ TROUBLESHOOTING
ISSUE: PocketBase SDK not loading ───────────────────────────────── Check: Is PocketBase script tag present in HTML?
<script src="https://cdn.jsdelivr.net/npm/pocketbase@0.26.5/dist/pocketbase.umd.js"></script>────────────────────────────────────────────────────────────────────────────
ISSUE: OAuth popup is blank or fails ──────────────────────────────────── Check:
- PocketBase is running on http://localhost:8090
- PocketBase has OAuth configured (Settings → OAuth2)
- PocketBase has Microsoft provider configured
- Redirect URI in PocketBase points to your app
────────────────────────────────────────────────────────────────────────────
ISSUE: Graph token is undefined ─────────────────────────────── Check:
- PocketBase OAuth config includes Graph scopes
- Scopes: offline_access, User.Read, Files.Read, etc.
- PocketBase returns token in authData.meta
────────────────────────────────────────────────────────────────────────────
ISSUE: Service tokens not working ────────────────────────────────── Check:
- Environment variables are set and exported
- Service account credentials are correct
- Credentials have required permissions in Azure/PocketBase
================================================================================ NEXT STEPS
- Verify auth works with manual testing
- Integrate actual business logic using tokens
- Add error handling and user feedback
- Test with real Microsoft Graph API calls
- Deploy to production with proper security
See UNIFIED_AUTH_GUIDE.md for complete documentation.