PAN Authentication System - Implementation Complete โ
What We Built
A complete JWT authentication system that eliminates manual header management - developers never write Authorization: Bearer ${token}.
Core Philosophy
"Auth should be invisible infrastructure" - Developers focus on features, not plumbing.๐ฆ Components Created
1. pan-auth.mjs (12KB)
Location: pan-auth.mjs
JWT authentication manager:
- โ Stores tokens in localStorage/sessionStorage/memory
- โ
Publishes retained
auth.state(for UI) - โ
Publishes retained
auth.internal.state(for connectors - includes token) - โ Handles login/logout/refresh flows
- โ Auto-refreshes tokens before expiry
- โ Decodes JWT to check expiration
- Commands:
auth.login,auth.logout,auth.refresh,auth.setToken,auth.check - Events:
auth.state,auth.login.success,auth.login.error,auth.logout.success,auth.refresh.success,auth.refresh.error
2. pan-fetch.mjs (3.8KB)
Location: pan-fetch.mjs
Authenticated fetch wrapper:
- โ
Drop-in replacement for
fetch() - โ
Auto-injects
Authorizationheader - โ
Convenience methods:
get(),post(),put(),patch(),delete() - โ
fetchJson()- auto JSON parse + error handling - โ
isAuthenticated()- check login status
import { panFetch } from '../pan-fetch.mjs';
const users = await panFetch.get('/api/users'); // Auth header auto-added!
3. Enhanced Connectors
#### pan-data-connector.mjs (Enhanced)
Location: pan-data-connector.mjs
- โ
Subscribes to
auth.internal.state - โ
Auto-injects
Authorization: Bearer ${token}in all fetch requests - โ Works with existing CRUD topics
pan-graphql-connector.mjs (Enhanced)
Location: pan-graphql-connector.mjs
- โ
Subscribes to
auth.internal.state - โ
Auto-injects
Authorization: Bearer ${token}in GraphQL requests - โ Works with existing GraphQL queries/mutations
4. Mock Auth API (6.8KB)
Location:examples/mock-auth-api.mjs
Client-side JWT simulator for testing:
- โ
Intercepts
/api/*requests - โ Simulates login/refresh/logout
- โ Creates & validates mock JWT tokens
- โ Test users included
admin@example.com/admin123user@example.com/user123demo@example.com/demo
5. Complete Demo (11KB)
Location:examples/auth-demo.html
Full working example:
- โ Login form with credentials
- โ Auth status display
- โ Protected user list (requires auth)
- โ Automatic header injection demonstration
- โ Beautiful UI with gradients
open examples/auth-demo.html
๐ Documentation
1. Complete Guide (17KB)
Location:docs/AUTHENTICATION.md
Comprehensive documentation:
- โ Quick start
- โ Architecture explanation
- โ Component API reference
- โ Topic reference
- โ Complete examples
- โ Token refresh guide
- โ Storage options
- โ Backend integration
- โ Security best practices
- โ Troubleshooting
- โ FAQ
2. Quick Start (4.1KB)
Location:docs/AUTH_QUICKSTART.md
TL;DR version:
- โ 30-second setup
- โ Login component
- โ Protected component
- โ Topics reference
- โ Testing instructions
๐ How It Works
Architecture
User Component Data Component
โ โ
โโโ auth.login โโโโโโโโโโโค
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ <pan-auth> โ โ Manages tokens
โ Auth State Manager โ
โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
โ
โโโ auth.state (public - no token)
โโโ auth.internal.state (private - has token)
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ <pan-connector> โ โ Auto-injects headers
โ Data Connector โ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
Authorization: Bearer ${token}
โ
โผ
Backend API
Flow
auth.login topic receives request, calls login APIauth.state + auth.internal.stateauth.internal.state, get tokenAuthorization: Bearer ${token} auto-refreshesauth.logout, tokens clearedโจ Key Features
1. Zero Manual Headers
Before:const response = await fetch('/api/users', {
headers: {
'Authorization': `Bearer ${token}` // โ Manual!
}
});
After:
pc.publish({ topic: 'users.list.get', data: {} }); // โ
Automatic!
2. Declarative Setup
<!-- Drop on page, done -->
<pan-auth storage="localStorage" auto-refresh="true"></pan-auth>
<pan-data-connector resource="users" base-url="/api"></pan-data-connector>
3. Component Ignorance
Components don't know auth exists:
customElements.define('user-list', class extends HTMLElement {
connectedCallback() {
const pc = new PanClient(this);
// Just request data - auth handled automatically
pc.publish({ topic: 'users.list.get', data: {} });
}
});
4. Auto Token Refresh
<pan-auth auto-refresh="true" refresh-before="300"></pan-auth>
<!-- Tokens refresh 5 minutes before expiry - no expired token errors! -->
5. Works Everywhere
- โ
REST APIs via
- โ
GraphQL via
- โ
Custom fetches via
panFetch - โ All connectors auto-inject headers
๐ Usage Examples
Minimal Setup
<!DOCTYPE html>
<meta charset="utf-8">
<script type="module" src="../pan-autoload.mjs"></script>
<pan-auth storage="localStorage" auto-refresh="true"></pan-auth>
<pan-data-connector resource="users" base-url="/api"></pan-data-connector>
<script type="module">
import { PanClient } from '../pan-client.mjs';
const pc = new PanClient();
// Login
await pc.request('auth.login', {
email: 'user@example.com',
password: 'pass123'
});
// Fetch data - auth header automatically added!
pc.publish({ topic: 'users.list.get', data: {} });
</script>
Login Component
customElements.define('login-form', class extends HTMLElement {
connectedCallback() {
const pc = new PanClient(this);
this.innerHTML = `
<form>
<input name="email" type="email" required>
<input name="password" type="password" required>
<button>Login</button>
</form>
`;
this.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const result = await pc.request('auth.login', {
email: formData.get('email'),
password: formData.get('password')
});
if (result.data.ok) {
alert('Logged in!');
}
});
}
});
Protected Component
customElements.define('user-list', class extends HTMLElement {
connectedCallback() {
const pc = new PanClient(this);
// React to auth state
pc.subscribe('auth.state', msg => {
if (msg.data.authenticated) {
pc.publish({ topic: 'users.list.get', data: {} });
} else {
this.innerHTML = '<p>Please log in</p>';
}
}, { retained: true });
// Display users
pc.subscribe('users.list.state', msg => {
this.innerHTML = `<ul>${msg.data.items.map(u =>
`<li>${u.name}</li>`
).join('')}</ul>`;
}, { retained: true });
}
});
Custom Fetch
import { panFetch } from '../pan-fetch.mjs';
// All automatically include auth header
const users = await panFetch.get('/api/users');
const newUser = await panFetch.post('/api/users', { name: 'John' });
const updated = await panFetch.put('/api/users/1', { name: 'Jane' });
await panFetch.delete('/api/users/1');
๐ฏ What Problem This Solves
The Manual Header Problem
Old way (error-prone):// Every component needs to:
const token = localStorage.getItem('token');
const response = await fetch('/api/users', {
headers: { 'Authorization': `Bearer ${token}` }
});
// Problems:
// - Easy to forget header
// - Repeated code everywhere
// - Token management in every component
// - No auto-refresh
// - Testing is hard
New way (automatic):
// Component just requests data
pc.publish({ topic: 'users.list.get', data: {} });
// Benefits:
// - Never forget headers
// - No token management code
// - Components are pure logic
// - Auto-refresh built-in
// - Easy to test (mock auth.state)
๐ Security
Best Practices Implemented
auth.state (public) - no token, safe for UI
- auth.internal.state (private) - has token, only for connectors
localStorage - persistent
- sessionStorage - temporary
- memory - no persistence (most secure)
๐งช Testing
Run the Demo
# Open auth demo
open examples/auth-demo.html
# Test with:
# - admin@example.com / admin123
# - user@example.com / user123
# - demo@example.com / demo
Mock API
The mock auth API (examples/mock-auth-api.mjs) provides:
- โ Client-side JWT simulation
- โ Token validation
- โ Login/refresh/logout endpoints
- โ
Protected
/api/usersendpoint - โ Test users included
Integration Testing
// Mock auth state in tests
pc.publish({
topic: 'auth.internal.state',
data: {
authenticated: true,
token: 'mock-token',
user: { id: 1, email: 'test@example.com' }
},
retain: true
});
// Now all requests include mock token
๐ Files Modified/Created
New Files
src/components/
โโโ pan-auth.mjs (12KB) - Auth manager component
โโโ pan-fetch.mjs (3.8KB) - Authenticated fetch wrapper
examples/
โโโ auth-demo.html (11KB) - Complete working demo
โโโ mock-auth-api.mjs (6.8KB) - Mock backend for testing
docs/
โโโ AUTHENTICATION.md (17KB) - Complete documentation
โโโ AUTH_QUICKSTART.md (4.1KB) - Quick reference
Modified Files
src/components/
โโโ pan-data-connector.mjs - Added auto auth header injection
โโโ pan-graphql-connector.mjs - Added auto auth header injection
Total: 6 new files, 2 enhanced files, ~55KB of code + docs
๐ Documentation
docs/AUTHENTICATION.md - Complete guide (read this first!)docs/AUTH_QUICKSTART.md - Quick reference (TL;DR version)examples/auth-demo.html - Working demo (try it!)๐ข Ready to Ship
What's Complete
- โ JWT authentication manager
- โ Auto header injection (REST + GraphQL)
- โ Authenticated fetch wrapper
- โ Auto token refresh
- โ Storage options (localStorage/sessionStorage/memory)
- โ Complete topic-based API
- โ Mock backend for testing
- โ Working demo
- โ Comprehensive documentation
Zero Dependencies
- โ No build step
- โ No TypeScript compilation
- โ No external auth libraries
- โ Just plain JavaScript ESM
Zero Configuration
- โ
Drop
on page - โ Set storage type
- โ Done!
๐ Summary
You wanted: A way to handle auth without manual header management. You got: A complete authentication system where developers never write auth headers. They just: on pageauth.login topic๐ Next Steps
open examples/auth-demo.htmldocs/AUTHENTICATION.mdStatus: โ Complete and ready to use!