Back to Skills

dashboard-api-authentication

majiayu000
Updated 2 days ago
58
9
58
View on GitHub
Metaapidata

About

This skill provides authentication patterns for dashboard frontend API calls, covering JWT token handling and the apiRequest helper. Use it when adding new endpoints, creating data-fetching components, or debugging 401 errors. It emphasizes using api.ts functions instead of raw fetch to ensure proper authentication headers are included.

Quick Install

Claude Code

Recommended
Plugin CommandRecommended
/plugin add https://github.com/majiayu000/claude-skill-registry
Git CloneAlternative
git clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/dashboard-api-authentication

Copy and paste this command in Claude Code to install this skill

Documentation

Dashboard API Authentication

Quick Reference

// ALWAYS use api.ts functions for authenticated requests
import { getFeatureFlags, setFeatureFlagOverride } from '../api';

// NEVER use raw fetch for authenticated endpoints
// BAD: fetch('/api/feature-flags', { credentials: 'include' })
// GOOD: getFeatureFlags()

The Golden Rule

All authenticated API calls MUST use the apiRequest helper from api.ts or one of the exported API functions that use it internally.

The dashboard uses JWT-based authentication. The token is stored in localStorage and sent via the Authorization: Bearer <token> header. Raw fetch() calls with credentials: 'include' will NOT include this header.

Authentication Architecture

Token Storage

// Token is stored in multiple places for different purposes:
localStorage.setItem('dashboard_token', token); // Primary storage
setCookie('auth_token', token, 1); // For nginx auth (production)

The apiRequest Helper

Located in packages/dashboard-frontend/src/api.ts:

async function apiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
  const token = getAuthToken(); // Gets from localStorage or cookie

  const headers: HeadersInit = {
    'Content-Type': 'application/json',
    ...options.headers,
  };

  if (token) {
    // THIS IS THE KEY - adds Authorization header
    (headers as Record<string, string>)['Authorization'] = `Bearer ${token}`;
  }

  const response = await fetch(`${API_BASE}${endpoint}`, {
    ...options,
    headers,
  });

  // Handles 401 by clearing token and reloading
  if (response.status === 401) {
    clearAuthToken();
    window.location.reload();
    throw new Error('Authentication expired');
  }

  // ... rest of response handling
}

Common Patterns

Adding a New API Function

// In packages/dashboard-frontend/src/api.ts

// For GET requests
export async function getMyData(): Promise<MyDataResponse> {
  return apiRequest('/my-endpoint');
}

// For POST/PUT/DELETE requests
export async function updateMyData(id: string, data: UpdateInput): Promise<MyDataResponse> {
  return apiRequest(`/my-endpoint/${encodeURIComponent(id)}`, {
    method: 'PUT',
    body: JSON.stringify(data),
  });
}

Using API Functions in Components

// In a component or hook
import { getMyData, updateMyData } from '../api';

// In useEffect or event handler
const loadData = async () => {
  try {
    const data = await getMyData(); // Automatically authenticated
    setData(data);
  } catch (error) {
    // Handle error - may be auth error, network error, etc.
  }
};

Using API Functions in Custom Hooks

// In packages/dashboard-frontend/src/hooks/useMyFeature.ts
import { useCallback, useEffect, useState } from 'react';
import { getMyData, type MyDataResponse } from '../api';

export function useMyFeature() {
  const [data, setData] = useState<MyDataResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const loadData = useCallback(async () => {
    try {
      setLoading(true);
      const result = await getMyData(); // Uses apiRequest internally
      setData(result);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to load');
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    loadData();
  }, [loadData]);

  return { data, loading, error, refresh: loadData };
}

Common Mistakes

Mistake 1: Using Raw Fetch

// BAD - Will get 401 Unauthorized
const response = await fetch('/api/feature-flags', {
  credentials: 'include', // This sends cookies, NOT the JWT header
});

// GOOD - Uses apiRequest which adds Authorization header
import { getFeatureFlags } from '../api';
const data = await getFeatureFlags();

Mistake 2: Forgetting to Export from api.ts

If you add a new endpoint, you must:

  1. Add the function to api.ts
  2. Export it
  3. Import it where needed
// In api.ts - add AND export
export async function getNewEndpoint(): Promise<Response> {
  return apiRequest('/new-endpoint');
}

// In component - import from api.ts
import { getNewEndpoint } from '../api';

Mistake 3: Not Handling 401 in Custom Fetch

If you absolutely must use raw fetch (rare), handle 401:

// Only if apiRequest can't be used (e.g., file uploads with progress)
const response = await fetch(url, {
  headers: {
    Authorization: `Bearer ${getAuthToken()}`,
  },
});

if (response.status === 401) {
  clearAuthToken();
  window.location.reload();
  throw new Error('Authentication expired');
}

Debugging 401 Errors

Symptoms

  • API calls return 401 Unauthorized
  • Server logs show "No authorization header"
  • Feature works initially but fails after page components load

Diagnostic Steps

  1. Check if using apiRequest:

    grep -r "fetch('/api" packages/dashboard-frontend/src/
    

    Any raw fetch to /api/* is suspicious.

  2. Check Network tab:

    • Look for Authorization header in request
    • If missing, the call isn't using apiRequest
  3. Verify token exists:

    // In browser console
    localStorage.getItem('dashboard_token');
    

Fix Pattern

Replace raw fetch with api.ts function:

// Before (broken)
const response = await fetch(`/api/feature-flags/${id}`, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({ enabled }),
});

// After (working)
import { setFeatureFlagOverride } from '../api';
await setFeatureFlagOverride(id, enabled);

Server-Side Authentication

The server expects JWT in the Authorization header:

// In packages/dashboard/src/auth.ts
authMiddleware = async (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    res.status(401).json({ error: 'No authorization header' });
    return;
  }

  const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader;

  const payload = this.verifyToken(token);
  // ... rest of verification
};

Checklist for New API Endpoints

When adding a new authenticated endpoint:

  • Create the route in packages/dashboard/src/server/routes/
  • Apply requireAuth middleware to the route
  • Add API function in packages/dashboard-frontend/src/api.ts
  • Export the function from api.ts
  • Use the exported function in components/hooks (never raw fetch)
  • Test that 401 is properly handled if token expires

Testing Authentication

To verify authentication is working:

// In a test or console
import { getAuthToken } from '../api';

// Check token exists
const token = getAuthToken();
console.log('Token present:', !!token);

// Make authenticated request
try {
  const data = await getFeatureFlags();
  console.log('Auth working:', data);
} catch (e) {
  console.error('Auth failed:', e);
}

GitHub Repository

majiayu000/claude-skill-registry
Path: skills/dashboard-api-authentication

Related Skills

content-collections

Meta

This skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.

View skill

creating-opencode-plugins

Meta

This skill provides the structure and API specifications for creating OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It offers implementation patterns for JavaScript/TypeScript modules that intercept and extend the AI assistant's lifecycle. Use it when you need to build event-driven plugins for monitoring, custom handling, or extending OpenCode's capabilities.

View skill

evaluating-llms-harness

Testing

This Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.

View skill

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill