Files
Prism-Notes-Main/auth-module/README.md
T
aewing 4c6058c656 Extract auth module: standalone PocketBase OAuth2 + Graph token management
- Create reusable auth-module/ with frontend and backend components
- Frontend: PocketBaseAuth class for OAuth2 login and token management
- Backend: GraphTokenManager and PocketBaseValidator for token operations
- Includes TypeScript types and comprehensive README
- No project-specific dependencies - ready to use in other projects
2026-01-16 03:49:52 +00:00

5.6 KiB

Auth Module

Standalone authentication module for PocketBase OAuth2 + Microsoft Graph integration.

Features

  • Frontend: PocketBase OAuth2 authentication with Microsoft provider
  • Backend: Microsoft Graph token management with automatic caching
  • No dependencies on project-specific code - Use in any project

Installation

Copy the auth-module folder to your project:

cp -r auth-module /path/to/your/project/

Install dependencies:

bun add pocketbase @azure/msal-node

Frontend Usage

Basic Setup

import { PocketBaseAuth } from './auth-module/frontend';

const auth = new PocketBaseAuth({
  pbUrl: 'http://localhost:8090',
  collection: 'Users',
  provider: 'microsoft',
  loginContainerId: 'loginContainer',
  userDisplayNameId: 'userDisplayName',
  userEmailId: 'userEmailValue',
  loginBtnId: 'loginBtn',
  loginErrorId: 'loginError',
});

HTML Required

<!-- Login container (shown when not authenticated) -->
<div id="loginContainer" class="hidden">
  <button id="loginBtn">Login with Microsoft</button>
  <div id="loginError" class="hidden"></div>
</div>

<!-- User display (shown when authenticated) -->
<div id="userDisplayName"></div>
<div id="userEmailValue"></div>

Event Callbacks

const auth = new PocketBaseAuth(config, {
  onAuthSuccess: (state) => {
    console.log('Logged in:', state.user.email);
    // Initialize other systems here
  },
  onAuthFailure: (error) => {
    console.error('Login failed:', error);
  },
  onTokenUpdate: (state) => {
    console.log('Token refreshed');
  },
  onUiUpdate: (state) => {
    console.log('UI updated');
  },
});

Methods

// Get current auth state
const state = auth.getAuthState();
console.log(state.isAuthenticated, state.user);

// Check token status
const status = await auth.checkTokenStatus();

// Logout
auth.logout();

// Get raw PocketBase instance if needed
const pb = auth.getPocketBase();

Backend Usage

Setup (server.ts)

import { GraphTokenManager, PocketBaseValidator, BackendAuth } from './auth-module/backend';

// Option 1: Use individual managers
const graphMgr = new GraphTokenManager({
  clientId: process.env.CLIENT_ID,
  tenantId: process.env.TENANT_ID,
  clientSecret: process.env.CLIENT_SECRET,
});

const pbValidator = new PocketBaseValidator('http://127.0.0.1:8090');

// Option 2: Use combined manager
const backendAuth = new BackendAuth({
  clientId: process.env.CLIENT_ID,
  tenantId: process.env.TENANT_ID,
  clientSecret: process.env.CLIENT_SECRET,
});

Get Graph Token

// Get token string
const token = await graphMgr.getToken();

// Get token with expiration
const { token, expiresOnISO } = await graphMgr.getTokenWithExpiry();

// Check if cached and valid
if (graphMgr.isTokenValid()) {
  console.log('Using cached token');
}

// Clear cache (force refresh)
graphMgr.clearCache();

Validate PocketBase Token

// Validate token
const isValid = await pbValidator.validateUserToken(token);

// Get user record
const user = await pbValidator.getUserRecord(token);

// Get PocketBase instance
const pb = pbValidator.getPocketBase();

Endpoint Example

app.get('/api/graph/status', async (c) => {
  try {
    const { token, expiresOnISO } = await graphMgr.getTokenWithExpiry();
    return c.json({
      active: true,
      expiresOnISO,
    });
  } catch (error) {
    return c.json(
      { active: false, error: error.message },
      500
    );
  }
});

app.post('/api/submit', async (c) => {
  const body = await c.req.json();
  const pbToken = body.pbToken;

  if (!pbToken) {
    return c.json({ error: 'Missing pbToken' }, 401);
  }

  const isValid = await pbValidator.validateUserToken(pbToken);
  if (!isValid) {
    return c.json({ error: 'Invalid token' }, 401);
  }

  // Token is valid, proceed with business logic
  // ...
});

Environment Variables

Required in .env file:

CLIENT_ID=your-microsoft-app-id
TENANT_ID=your-azure-tenant-id
CLIENT_SECRET=your-microsoft-client-secret
PB_DB=http://127.0.0.1:8090  # PocketBase URL

Configuration

Frontend Config Options

interface AuthConfig {
  pbUrl?: string;                    // PocketBase URL (default: http://localhost:8090)
  collection?: string;                // Auth collection (default: Users)
  provider?: string;                  // OAuth provider (default: microsoft)
  loginContainerId?: string;          // ID of login container element
  userDisplayNameId?: string;         // ID of user name display element
  userEmailId?: string;               // ID of user email display element
  loginBtnId?: string;                // ID of login button element
  loginErrorId?: string;              // ID of error message element
}

Backend Config Options

interface BackendAuthConfig {
  clientId?: string;      // Microsoft app ID (or CLIENT_ID env var)
  tenantId?: string;      // Azure tenant ID (or TENANT_ID env var)
  clientSecret?: string;  // Client secret (or CLIENT_SECRET env var)
}

TypeScript

The module exports TypeScript types for type safety:

import { AuthState, AuthConfig, GraphTokenCache } from './auth-module/types';

const state: AuthState = auth.getAuthState();

Notes

  • Frontend manages user authentication only
  • Backend manages Graph API tokens (never exposed to client)
  • Tokens are cached in-memory on backend with automatic refresh
  • Module does not include project-specific features (alerts, etc.)
  • Each project can implement its own business logic on top

License

Same as parent project