WS3 — Advanced Analytics Dashboard: - DB migration: analytics_events table (tenant_id, date, metric_type, count) - AnalyticsService: recordEvent (fire-and-forget), getTokenTrend, getAgentActivity, getAgentUsageSummary - Analytics hooks in OAuth2Service (token_issued) and AgentService (agent_registered/deactivated) - AnalyticsController + routes/analytics.ts (gated by ANALYTICS_ENABLED flag) - Portal: TokenTrendChart (recharts LineChart), AgentHeatmap (recharts heatmap), /analytics page WS4 — API Gateway Tiers: - DB migration: tenant_tiers table; src/config/tiers.ts (free/pro/enterprise limits) - TierService: getStatus, initiateUpgrade (Stripe), applyUpgrade; TierLimitError in errors.ts - tierEnforcement middleware (Redis-backed daily call/token counters; TIER_ENFORCEMENT flag) - Agent count enforcement in AgentService.create() - Stripe webhook updated to call TierService.applyUpgrade() on checkout.session.completed - TierController + routes/tiers.ts; Portal: /settings/tier page with upgrade flow WS6 — AGNTCY Compliance Certification: - ComplianceService: generateReport() (Redis-cached 5 min), exportAgentCards() - Compliance sections: agent-identity (DID + credential expiry checks), audit-trail (Merkle chain) - ComplianceController updated with getComplianceReport, exportAgentCards handlers - routes/compliance.ts: new AGNTCY routes (gated by COMPLIANCE_ENABLED flag); SOC2 routes unaffected QA: - 28 new unit tests: AnalyticsService (8), TierService (9), ComplianceService (11) — all pass - 673 total unit tests passing; 0 TypeScript errors across API and portal - AGNTCY conformance test suite at tests/agntcy-conformance/ (4 protocol tests) - Portal builds cleanly: 9 routes including /analytics and /settings/tier - Feature flags verified: ANALYTICS_ENABLED, TIER_ENFORCEMENT, COMPLIANCE_ENABLED Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* useAuth — Client-side authentication hook for the SentryAgent portal.
|
|
*
|
|
* Reads the tenant JWT stored in localStorage under the key
|
|
* `sentryagent_token`. If no token is present the hook signals that the user
|
|
* is unauthenticated so the calling page can redirect to `/login`.
|
|
*
|
|
* This is intentionally lightweight: the portal calls the AgentIdP API
|
|
* directly; the JWT is issued by the AgentIdP `/api/tenants/login` endpoint
|
|
* and stored on successful sign-in.
|
|
*
|
|
* @module hooks/useAuth
|
|
*/
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
/** The localStorage key under which the tenant JWT is persisted. */
|
|
export const AUTH_TOKEN_KEY = 'sentryagent_token';
|
|
|
|
/** Shape returned by the useAuth hook. */
|
|
export interface AuthState {
|
|
/** The stored JWT, or null if unauthenticated. */
|
|
token: string | null;
|
|
/** True while the hook is reading from localStorage on mount. */
|
|
loading: boolean;
|
|
/**
|
|
* Sign the user out by removing the stored token and redirecting to /login.
|
|
*/
|
|
signOut: () => void;
|
|
}
|
|
|
|
/**
|
|
* Returns the current authentication state and provides a sign-out helper.
|
|
* Redirects to `/login` when no token is found (after the initial mount check).
|
|
*
|
|
* @param redirectOnUnauth - When true (default), redirects to /login if
|
|
* no token is present. Pass false on public pages.
|
|
* @returns AuthState
|
|
*/
|
|
export function useAuth(redirectOnUnauth = true): AuthState {
|
|
const router = useRouter();
|
|
const [token, setToken] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const stored =
|
|
typeof window !== 'undefined'
|
|
? localStorage.getItem(AUTH_TOKEN_KEY)
|
|
: null;
|
|
setToken(stored);
|
|
setLoading(false);
|
|
|
|
if (!stored && redirectOnUnauth) {
|
|
router.replace('/login');
|
|
}
|
|
}, [redirectOnUnauth, router]);
|
|
|
|
const signOut = (): void => {
|
|
if (typeof window !== 'undefined') {
|
|
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
}
|
|
setToken(null);
|
|
router.replace('/login');
|
|
};
|
|
|
|
return { token, loading, signOut };
|
|
}
|