import { describe, it, expect } from 'vitest'; import { authenticateRequest } from './auth.js'; import type { IncomingMessage } from 'http'; function mockRequest(headers: Record = {}): IncomingMessage { return { headers } as unknown as IncomingMessage; } function mockRequestWithUrl(url: string, headers: Record = {}): IncomingMessage { return { url, headers } as unknown as IncomingMessage; } describe('authenticateRequest', () => { describe('no auth configured', () => { it('allows all connections', () => { const result = authenticateRequest(mockRequest(), {}); expect(result.authenticated).toBe(true); expect(result.identity).toBe('anonymous'); }); }); describe('token auth', () => { const config = { token: 'secret-token-123' }; it('accepts valid Bearer token', () => { const result = authenticateRequest( mockRequest({ authorization: 'Bearer secret-token-123' }), config, ); expect(result.authenticated).toBe(true); expect(result.identity).toBe('token-user'); }); it('rejects missing Authorization header', () => { const result = authenticateRequest(mockRequest(), config); expect(result.authenticated).toBe(false); expect(result.error).toContain('Authorization required'); }); it('rejects invalid token', () => { const result = authenticateRequest( mockRequest({ authorization: 'Bearer wrong-token' }), config, ); expect(result.authenticated).toBe(false); expect(result.error).toContain('Invalid token'); }); it('rejects non-Bearer format', () => { const result = authenticateRequest( mockRequest({ authorization: 'Basic dXNlcjpwYXNz' }), config, ); expect(result.authenticated).toBe(false); expect(result.error).toContain('Invalid Authorization format'); }); it('uses Tailscale identity when both token and tailscale are configured', () => { const result = authenticateRequest( mockRequest({ authorization: 'Bearer secret-token-123', 'tailscale-user-login': 'will@example.com', }), { token: 'secret-token-123', tailscaleIdentity: true }, ); expect(result.authenticated).toBe(true); expect(result.identity).toBe('will@example.com'); }); }); describe('tailscale identity', () => { const config = { tailscaleIdentity: true }; it('extracts identity from Tailscale-User-Login header', () => { const result = authenticateRequest( mockRequest({ 'tailscale-user-login': 'will@example.com' }), config, ); expect(result.authenticated).toBe(true); expect(result.identity).toBe('will@example.com'); }); it('allows connections without Tailscale header (local access)', () => { const result = authenticateRequest(mockRequest(), config); expect(result.authenticated).toBe(true); expect(result.identity).toBe('anonymous'); }); }); describe('query parameter token', () => { const config = { token: 'secret-token-123' }; it('accepts valid token in query parameter', () => { const result = authenticateRequest( mockRequestWithUrl('/?token=secret-token-123'), config, ); expect(result.authenticated).toBe(true); expect(result.identity).toBe('token-user'); }); it('rejects invalid token in query parameter', () => { const result = authenticateRequest( mockRequestWithUrl('/?token=wrong'), config, ); expect(result.authenticated).toBe(false); expect(result.error).toContain('Invalid token'); }); it('prefers header over query parameter', () => { const result = authenticateRequest( mockRequestWithUrl('/?token=wrong', { authorization: 'Bearer secret-token-123' }), config, ); expect(result.authenticated).toBe(true); expect(result.identity).toBe('token-user'); }); it('rejects when neither header nor query parameter provided', () => { const result = authenticateRequest( mockRequestWithUrl('/'), config, ); expect(result.authenticated).toBe(false); expect(result.error).toContain('Authorization required'); }); }); });