4c6058c656
- 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
245 lines
5.6 KiB
Markdown
245 lines
5.6 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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```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('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
|
|
|
|
```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=http://127.0.0.1:8090 # PocketBase URL
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Frontend Config Options
|
|
|
|
```typescript
|
|
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
|
|
|
|
```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
|