Files
2026-01-10 19:23:47 +00:00

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:

  1. Add PocketBase SDK
  2. Add auth module
  3. Initialize auth on page load
  4. 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:

  1. Import auth module
  2. Initialize backend auth
  3. Add service token endpoints
  4. 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

  1. Open http://localhost:3000 in browser
  2. Should see "Sign in with Microsoft" button
  3. Click to authenticate via PocketBase OAuth
  4. 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:

  1. PocketBase is running on http://localhost:8090
  2. PocketBase has OAuth configured (Settings → OAuth2)
  3. PocketBase has Microsoft provider configured
  4. Redirect URI in PocketBase points to your app

────────────────────────────────────────────────────────────────────────────

ISSUE: Graph token is undefined ─────────────────────────────── Check:

  1. PocketBase OAuth config includes Graph scopes
  2. Scopes: offline_access, User.Read, Files.Read, etc.
  3. PocketBase returns token in authData.meta

────────────────────────────────────────────────────────────────────────────

ISSUE: Service tokens not working ────────────────────────────────── Check:

  1. Environment variables are set and exported
  2. Service account credentials are correct
  3. Credentials have required permissions in Azure/PocketBase

================================================================================ NEXT STEPS

  1. Verify auth works with manual testing
  2. Integrate actual business logic using tokens
  3. Add error handling and user feedback
  4. Test with real Microsoft Graph API calls
  5. Deploy to production with proper security

See UNIFIED_AUTH_GUIDE.md for complete documentation.