Home / AUTHENTICATION_SUMMARY

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
Topics:
  • 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 Authorization header
  • โœ… Convenience methods: get(), post(), put(), patch(), delete()
  • โœ… fetchJson() - auto JSON parse + error handling
  • โœ… isAuthenticated() - check login status
Usage:
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
Test Credentials:
  • admin@example.com / admin123
  • user@example.com / user123
  • demo@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
Try it:
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

  • User logs in via auth.login topic
  • receives request, calls login API
  • On success, stores token and publishes auth.state + auth.internal.state
  • Connectors subscribe to auth.internal.state, get token
  • All subsequent requests automatically include Authorization: Bearer ${token}
  • Before token expires, auto-refreshes
  • User logs out via auth.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

  • โœ… Two-tier state
  • - auth.state (public) - no token, safe for UI - auth.internal.state (private) - has token, only for connectors
  • โœ… Token storage options
  • - localStorage - persistent - sessionStorage - temporary - memory - no persistence (most secure)
  • โœ… Auto-refresh
  • - Prevents expired token errors - Configurable refresh timing
  • โœ… HTTPS ready
  • - Works securely over HTTPS - Token never exposed in URL
  • โœ… Backend validation
  • - Tokens validated on backend - Short-lived access tokens - Long-lived refresh tokens

    ๐Ÿงช 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/users endpoint
    • โœ… 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!)
  • Inline docs - All components have JSDoc comments

  • ๐Ÿšข 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:
  • Drop on page
  • Login via auth.login topic
  • Make requests - headers auto-injected
  • Philosophy achieved: Auth is now invisible infrastructure, exactly as it should be.

    ๐Ÿ“ž Next Steps

  • Try the demo: open examples/auth-demo.html
  • Read the docs: docs/AUTHENTICATION.md
  • Integrate with your backend: See "Backend Integration" section
  • Test in your app: Replace manual headers with

  • Status: โœ… Complete and ready to use!