All files / api/src/middlewares auth.ts

92.1% Statements 35/38
93.75% Branches 15/16
100% Functions 1/1
92.1% Lines 35/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 691x           1x   1x   1x                           1x 24x   24x   65x 3x 3x 3x   62x 62x 62x     65x           60x 63x 14x 14x 14x     46x 63x 26x 26x 26x 23x 22x 22x 26x 19x 19x 19x 26x   27x 27x  
import {
  getAccessPassword,
  getAuthJwtSecret,
  getBearerToken,
} from '@api/constants';
import type { AppEnv } from '@api/types/hono';
import { AUTH_COOKIE_NAME } from '@api/utils/auth-cookie';
import type { MiddlewareHandler } from 'hono';
import { getCookie } from 'hono/cookie';
import type { Factory } from 'hono/factory';
import { jwt } from 'hono/jwt';
 
/**
 * Authenticated middleware for API requests.
 *
 * Blocks unauthenticated requests when either ACCESS_PASSWORD or BEARER_TOKEN is configured.
 *
 * Authentication is checked in this order:
 * 1. JWT cookie (access_token) — set by the login flow when ACCESS_PASSWORD is configured
 * 2. Bearer token header — for programmatic API access
 *
 * If neither ACCESS_PASSWORD nor BEARER_TOKEN is configured, all requests are allowed through.
 * Auth endpoints (/v1/super-agents/auth/*) are always exempt.
 */
export const authenticatedMiddleware = (
  factory: Factory<AppEnv>,
): MiddlewareHandler =>
  factory.createMiddleware(async (c, next) => {
    // Allow access to auth endpoints so that we can login or verify authorization
    if (c.req.path.startsWith('/v1/super-agents/auth/')) {
      await next();
      return;
    }
 
    const jwtSecret = getAuthJwtSecret(c);
    const bearerToken = getBearerToken(c);
    const accessPassword = getAccessPassword(c);
 
    // If neither ACCESS_PASSWORD nor BEARER_TOKEN is configured, skip auth
    if (!accessPassword && !bearerToken) {
      await next();
      return;
    }
 
    // Check JWT cookie first (set by login flow)
    const accessTokenCookie = getCookie(c, AUTH_COOKIE_NAME);
    if (accessTokenCookie) {
      await jwt({ cookie: AUTH_COOKIE_NAME, secret: jwtSecret })(c, next);
      return;
    }
 
    // Check Bearer token header (for programmatic access)
    const bearerHeaderString = c.req.header('authorization');
    if (bearerHeaderString) {
      const parts = bearerHeaderString.split(' ');
      if (
        parts.length === 2 &&
        parts[0].toLowerCase() === 'bearer' &&
        bearerToken &&
        parts[1] === bearerToken
      ) {
        await next();
        return;
      }
    }
 
    c.res = c.text('Unauthorized', 401);
  });