# Koa.js Middleware Chain for Authentication and Token Management in Lemon AI

> Discover how Lemon AI's Koa.js middleware chain handles authentication and token management. Learn about bearer token extraction, global storage, and extensible auth gates for API requests.

- Repository: [hexdocom/lemonai](https://github.com/hexdocom/lemonai)
- Tags: architecture
- Published: 2026-03-03

---

**Lemon AI implements a deterministic Koa.js middleware pipeline that extracts bearer tokens from HTTP headers, stores them in a global singleton, and routes requests through an extensible authentication gate before dispatching to API handlers.**

The open-source Lemon AI project (`hexdocom/lemonai`) leverages Koa 2 to build its HTTP server, utilizing a carefully ordered middleware chain to handle authentication and token management. This architecture separates concerns between request preprocessing, token extraction, and authorization gating, making it straightforward to swap stub implementations with production-grade security logic.

## Middleware Pipeline Architecture

The entry point at [`src/app.js`](https://github.com/hexdocom/lemonai/blob/main/src/app.js) registers eleven middleware layers in a specific sequence. This ordering ensures that request bodies are parsed and logging is active before authentication logic executes.

The pipeline flows as follows:

1. **Response Wrapping** – `wrapContext` from [`src/middlewares/wrap.context.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/wrap.context.js) attaches `ctx.response.success`, `ctx.response.fail`, and `ctx.response.file` helpers to every context.
2. **Body Parsing** – `koaBody` handles multipart and JSON payloads, populating `ctx.request.body`.
3. **JSON Formatting** – Pretty-prints JSON responses.
4. **Request Logging** – `koa-logger` and a custom URL logger output method and path to the console.
5. **Static Assets** – `koa-static` serves files from the `public` directory.
6. **Timing** – Measures and logs request latency.
7. **Token Extraction** – `setGlobalTokenMiddleware` reads the `Authorization` header.
8. **Authentication Gate** – `authMiddleware` validates (or stubs) the token and sets `ctx.state.user`.
9. **Routing** – Dispatches to feature routers under `/api/`.
10. **API Documentation** – Serves Swagger UI via `koaSwagger`.

### Token Extraction and Global Storage

Before the authentication gate executes, the `setGlobalTokenMiddleware` in [`src/middlewares/setGlobalToken.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/setGlobalToken.js) intercepts the request to extract bearer tokens.

The middleware checks for an `Authorization` header beginning with `Bearer `, splits the token value, and stores it in a global singleton defined in [`src/globals.js`](https://github.com/hexdocom/lemonai/blob/main/src/globals.js):

```javascript
// src/middlewares/setGlobalToken.js
const globals = require('@src/globals');

module.exports = () => {
  return async (ctx, next) => {
    const authHeader = ctx.request.headers.authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      const token = authHeader.split(' ')[1];
      globals.setToken(token);
    }
    await next();
  };
};

```

The [`globals.js`](https://github.com/hexdocom/lemonai/blob/main/globals.js) module provides a simple state container:

```javascript
// src/globals.js
let currentToken = null;

module.exports = {
  setToken: (token) => { currentToken = token; },
  getToken: () => currentToken,
};

```

This design allows downstream middleware and route handlers to access the token via `globals.getToken()` without parsing headers repeatedly.

### Authentication Gate Implementation

The `authMiddleware` in [`src/middlewares/auth.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/auth.js) serves as the security checkpoint. In the current implementation, it acts as a stub that assigns a default user object while skipping actual token validation.

The middleware defines an `excludePatterns` array for paths that should bypass authentication:

```javascript
// src/middlewares/auth.js
const excludePatterns = [
  '/api/agent_store/last/'
];

module.exports = () => {
  return async (ctx, next) => {
    // Stub: always sets a default user
    ctx.state.user = { id: 1 };
    await next();
  };
};

```

While the stub currently ignores the `excludePatterns` array, the structure is in place to support conditional bypassing. The `ctx.state.user` object becomes available to all subsequent route handlers, enabling user-scoped logic.

## Extending the Chain for Production Authentication

To replace the stub with real JWT or OAuth validation, modify [`src/middlewares/auth.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/auth.js) while preserving the existing middleware order. The token is already available via `globals.getToken()` or directly from `ctx.request.headers.authorization`.

Here is a production-ready implementation using `jsonwebtoken`:

```javascript
// src/middlewares/auth.js (production implementation)
const jwt = require('jsonwebtoken');
const globals = require('@src/globals');

const excludePatterns = ['/api/agent_store/last/'];

module.exports = () => {
  return async (ctx, next) => {
    const path = ctx.path;
    
    // Skip auth for excluded paths
    if (excludePatterns.some(p => path.startsWith(p))) {
      return next();
    }

    const token = globals.getToken();
    if (!token) {
      return ctx.response.fail(null, 'Authentication required', 401);
    }

    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET);
      ctx.state.user = { 
        id: payload.sub, 
        roles: payload.roles || [] 
      };
      await next();
    } catch (err) {
      return ctx.response.fail(null, 'Invalid or expired token', 401);
    }
  };
};

```

Downstream route handlers can then access the authenticated user:

```javascript
// src/routers/user/users.js (excerpt)
router.get('/profile', async (ctx) => {
  const userId = ctx.state.user?.id;
  if (!userId) {
    return ctx.response.fail(null, 'Unauthenticated', 401);
  }

  const profile = await UserService.getProfile(userId);
  ctx.response.success(profile);
});

```

## Summary

- **Linear Pipeline**: Lemon AI registers eleven middleware layers in [`src/app.js`](https://github.com/hexdocom/lemonai/blob/main/src/app.js), ensuring request parsing and logging occur before authentication logic.
- **Token Extraction**: `setGlobalTokenMiddleware` extracts bearer tokens from the `Authorization` header and stores them in a global singleton ([`src/globals.js`](https://github.com/hexdocom/lemonai/blob/main/src/globals.js)), making the token accessible throughout the request lifecycle.
- **Authentication Gate**: `authMiddleware` ([`src/middlewares/auth.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/auth.js)) currently acts as a stub that assigns a default user object, but is structurally prepared for JWT/OAuth validation with an `excludePatterns` array for public routes.
- **Extensibility**: The middleware chain is designed for easy replacement of the authentication stub with production-grade validation without altering the overall pipeline structure.

## Frequently Asked Questions

### How does Lemon AI extract and store authentication tokens?

The `setGlobalTokenMiddleware` in [`src/middlewares/setGlobalToken.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/setGlobalToken.js) reads the `Authorization` header, checks for the `Bearer ` prefix, and stores the token string in a global singleton defined in [`src/globals.js`](https://github.com/hexdocom/lemonai/blob/main/src/globals.js). This makes the token available to any downstream middleware or route handler via `globals.getToken()`.

### Where is the authentication logic located in the middleware chain?

The authentication gate is implemented in [`src/middlewares/auth.js`](https://github.com/hexdocom/lemonai/blob/main/src/middlewares/auth.js) and is registered after the token extraction middleware in [`src/app.js`](https://github.com/hexdocom/lemonai/blob/main/src/app.js). This ensures the token is already available in the global store before the authentication middleware attempts validation. The current implementation is a stub that assigns a default user, but the structure supports JWT or OAuth integration.

### Can I exclude specific routes from authentication?

Yes. The [`auth.js`](https://github.com/hexdocom/lemonai/blob/main/auth.js) middleware includes an `excludePatterns` array that lists paths which should bypass authentication checks. While the current stub implementation does not actively filter these paths, the data structure is in place to support conditional skipping for public endpoints like `/api/agent_store/last/`.

### How do I access the authenticated user in route handlers?

After the authentication middleware executes, the user object is attached to `ctx.state.user`. Route handlers can access this property to retrieve the user ID and roles. For example, `const userId = ctx.state.user?.id;` retrieves the identifier set during authentication, enabling user-scoped database queries and authorization checks.