1fdc87f7f4
- Created Mode1 as self-contained test environment - Added auth-module copy with self-contained configuration - Implemented backend server with Graph token and PocketBase validation endpoints - Added frontend test dashboard with token comparison functionality - Fixed TypeScript compatibility issues (replaced import.meta.dir with __dirname) - Implemented actual PB token validation in comparison endpoint (no placeholders) - All endpoints tested and working with real tokens
249 lines
5.9 KiB
Markdown
249 lines
5.9 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
cp -r auth-module /path/to/your/project/
|
|
```
|
|
|
|
Install dependencies:
|
|
|
|
```bash
|
|
bun add pocketbase @azure/msal-node
|
|
```
|
|
|
|
## Frontend Usage
|
|
|
|
### Basic Setup
|
|
|
|
**Important**: The frontend is browser-side code. It must receive `pbUrl` from the consuming application (not from the backend).
|
|
|
|
```typescript
|
|
import { PocketBaseAuth } from './auth-module/frontend';
|
|
|
|
// In your project, load PB_URL from environment/config and pass it to frontend
|
|
const auth = new PocketBaseAuth({
|
|
pbUrl: process.env.PB_URL, // Required - must be provided by consuming application
|
|
collection: 'Users',
|
|
provider: 'microsoft',
|
|
loginContainerId: 'loginContainer',
|
|
userDisplayNameId: 'userDisplayName',
|
|
userEmailId: 'userEmailValue',
|
|
loginBtnId: 'loginBtn',
|
|
loginErrorId: 'loginError',
|
|
});
|
|
```
|
|
|
|
### HTML Required
|
|
|
|
```html
|
|
<!-- 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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```typescript
|
|
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(process.env.PB_DB);
|
|
|
|
// Option 2: Use combined manager
|
|
const backendAuth = new BackendAuth({
|
|
clientId: process.env.CLIENT_ID,
|
|
tenantId: process.env.TENANT_ID,
|
|
clientSecret: process.env.CLIENT_SECRET,
|
|
}, process.env.PB_DB);
|
|
```
|
|
|
|
### Get Graph Token
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```env
|
|
CLIENT_ID=your-microsoft-app-id
|
|
TENANT_ID=your-azure-tenant-id
|
|
CLIENT_SECRET=your-microsoft-client-secret
|
|
PB_DB=https://your-pocketbase-instance.com
|
|
PB_URL=https://your-pocketbase-instance.com
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Frontend Config Options
|
|
|
|
```typescript
|
|
interface AuthConfig {
|
|
pbUrl: string; // PocketBase URL (required)
|
|
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
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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
|