Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 144 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,124 +1,198 @@
# Map Colonies typescript service template
# Auth Manager BFF

----------------------------------
A Backend-For-Frontend (BFF) service that acts as the single entry point for the `auth-ui`. It routes traffic to the `auth-manager` service and multiple environment-specific Open Policy Agent (OPA) servers.

This is a basic repo template for building new MapColonies web services in Typescript.
## Overview

> [!IMPORTANT]
> To regenerate the types on openapi change run the command `npm run generate:openapi-types`.
The BFF shields the frontend from backend complexity — the UI never needs to know how many backends exist, where they are, or whether they are enabled on a given deployment. The BFF handles routing, guards, error normalisation, and capability discovery on the UI's behalf.

> [!WARNING]
> After creating a new repo based on this template, you should delete the CODEOWNERS file.
## Tech Stack

| Concern | Choice |
|---|---|
| Runtime | Node.js with TypeScript |
| Framework | Express (via `ts-server-boilerplate`) |
| Dependency Injection | TSyringe |
| Proxying | `http-proxy-middleware` |
| Request Validation | `express-openapi-validator` against `openapi3.yaml` |
| Configuration | `@map-colonies/config` |
| Observability | `@map-colonies/telemetry` (OpenTelemetry) |

## Development
When in development you should use the command `npm run start:dev`. The main benefits are that it enables offline mode for the config package, and source map support for NodeJS errors.

### Template Features:

- eslint configuration by [@map-colonies/eslint-config](https://github.com/MapColonies/eslint-config)

- prettier configuration by [@map-colonies/prettier-config](https://github.com/MapColonies/prettier-config)

- jest

- .nvmrc
## API

- Multi stage production-ready Dockerfile
### `GET /capabilities`

- commitlint
Returns the current state of the BFF so the `auth-ui` can adapt its interface at startup.

- git hooks
```json
{
"site": "AZURE",
"environments": ["prod", "integration", "qa"],
"features": {
"managerEnabled": true,
"opaEnabled": true
}
}
```

- logging by [@map-colonies/js-logger](https://github.com/MapColonies/js-logger)
### `/manager/*` — Auth Manager Proxy

- OpenAPI request validation
Proxies all traffic transparently to the `auth-manager` service. The `/manager` prefix is stripped before forwarding.

- config load with [node-config](https://www.npmjs.com/package/node-config)
```
GET /manager/client → GET http://auth-manager.internal:8080/client
```

- Tracing and metrics by [@map-colonies/telemetry](https://github.com/MapColonies/telemetry)
| Condition | Response |
|---|---|
| `manager.enabled: false` | `503 { "message": "Auth Manager capabilities are disabled on this node" }` |
| Manager unreachable | `502 { "message": "Auth Manager is currently unreachable" }` |

- github templates
### `/opa/{environment}/evaluate/*` — OPA Proxy

- bug report
Proxies evaluation requests to the appropriate OPA server. Applies guards before forwarding.

- feature request
```
POST /opa/prod/evaluate/authz/allow → POST http://opa-prod:8181/v1/data/authz/allow
```

- pull request
| Guard | Condition | Response |
|---|---|---|
| Toggle | `opa.enabled: false` | `503` |
| Method filter | Any method except `GET` / `POST` | `405` |
| Environment validation | Environment not in config | `404` |
| Proxy error | OPA server unreachable | `502 { message, environment, targetUrl }` |

- github actions
Only `GET` and `POST` are permitted — `PUT`, `PATCH`, and `DELETE` are blocked to prevent policy modification through the BFF.

- on pull_request
## Project Structure

- LGTM
```
src/
capabilities/
controllers/capabilitiesController.ts HTTP layer
models/capabilitiesManager.ts Business logic
routes/capabilitiesRouter.ts Route wiring
manager/
middleware/managerMiddleware.ts Toggle check, auth placeholder
routes/managerRouter.ts Proxy configuration
opa/
middleware/opaMiddleware.ts Toggle, method filter, env validation, auth placeholder
routes/opaRouter.ts Proxy configuration
common/
config.ts Unified config (boilerplate + BFF fields)
constants.ts DI service tokens
containerConfig.ts DI container wiring
serverBuilder.ts Express setup and middleware chain
config/
default.json Local development defaults
openapi3.yaml API contract (capabilities endpoint)
```

- test
## Configuration

All configuration is managed through `@map-colonies/config`. In development, it reads from `config/default.json`. In production, it connects to the remote config server.

### BFF-specific fields

```json
{
"site": "AZURE",
"cors": {
"allowedDomains": ["http://localhost:3000", "https://auth.mapcolonies.net"]
},
"manager": {
"enabled": true,
"url": "http://auth-manager.internal:8080"
},
"opa": {
"enabled": true,
"servers": {
"prod": "http://opa-prod.internal:8181",
"integration": "http://opa-int.internal:8181",
"qa": "http://opa-qa.internal:8181"
}
}
}
```

- lint
The `manager.enabled` and `opa.enabled` flags allow individual proxy routes to be disabled per deployment without a code change or redeployment of the UI.

- snyk
## Development

## API
Checkout the OpenAPI spec [here](/openapi3.yaml)
### Prerequisites

## Installation
- Node.js (see `.nvmrc` for version)
- npm

Install deps with npm
### Install dependencies

```bash
npm install
```

## Run Locally

Clone the project
### Run locally

```bash

git clone https://link-to-project

npm run start:dev
```

Go to the project directory
This enables `CONFIG_OFFLINE_MODE`, which reads configuration from `config/default.json` instead of connecting to the remote config server.

```bash

cd my-project

```

Install dependencies
### Run tests

```bash

npm install

npm run test # all tests
npm run test:unit # unit tests only
npm run test:integration # integration tests only
```

Start the server
### Regenerate OpenAPI types

Run this after any change to `openapi3.yaml`:

```bash
npm run generate:openapi-types
```

npm run start
## Request Flow

```
Request
├── CORS middleware validates origin against cors.allowedDomains
├── Metrics + HTTP logger
├── Compression
├── Body parser scoped to /capabilities only
├── OpenAPI validator native routes only — /manager and /opa are bypassed
├── /capabilities ──────────► CapabilitiesController → CapabilitiesManager → config
├── /manager/* ─────────────► managerEnabledMiddleware → authMiddleware → proxy
│ │
│ http://auth-manager
└── /opa/{env}/evaluate/* ──► opaEnabledMiddleware → methodFilterMiddleware
→ environmentMiddleware → authMiddleware → proxy
http://opa-{env}:8181
/v1/data/{policy path}
```

## Running Tests
## OpenAPI Spec

To run tests, run the following command
The `openapi3.yaml` file is the source of truth for native BFF endpoints. Proxy routes (`/manager/*`, `/opa/*`) are intentionally excluded from the spec — their contracts belong to `auth-manager` and OPA respectively.

```bash
The interactive Swagger UI is available at `/docs` when the server is running.

npm run test
## Future: Authentication Phase

```
An `authMiddleware` placeholder is already in place on both the `/manager/*` and `/opa/*` routes, currently passing all requests through. In the next phase this middleware will be populated to validate JWTs, making the BFF the Policy Enforcement Point for the `auth-ui`. No structural changes to the router or proxy logic will be required.

To only run unit tests:
```bash
npm run test:unit
```
## Deployment

The service is deployed as a Kubernetes workload using the Helm chart in the `helm/` directory. Configuration is injected per environment via Helm values — the Docker image is identical across all environments.

To only run integration tests:
```bash
npm run test:integration
helm upgrade --install auth-manager-bff ./helm -f helm/values-prod.yaml
```
18 changes: 18 additions & 0 deletions config/bff.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"site": "AZURE",
"cors": {
"allowedDomains": ["http://localhost:3000", "https://auth.mapcolonies.net"]
},
"manager": {
"enabled": true,
"url": "https://auth-manager-infra-services.apps.j1lk3njp.eastus.aroapp.io"
},
"opa": {
"enabled": true,
"servers": {
"dev": "http://opa-dev-opa-service.infra-services.svc.cluster.local:8181",
"int": "http://opa-int-opa-service.infra-services.svc.cluster.local:8181",
"qa": "http://opa-qa-opa-service.infra-services.svc.cluster.local:8181"
}
}
}
35 changes: 19 additions & 16 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,33 @@
},
"telemetry": {
"metrics": {},
"tracing": {
"isEnabled": false
},
"tracing": { "isEnabled": false },
"shared": {},
"logger": {
"level": "info",
"prettyPrint": false,
"opentelemetryOptions": {
"enabled": false
}
"opentelemetryOptions": { "enabled": false }
}
},
"server": {
"port": 8080,
"request": {
"payload": {
"limit": "1mb"
}
},
"response": {
"compression": {
"enabled": true,
"options": null
}
"request": { "payload": { "limit": "1mb" } },
"response": { "compression": { "enabled": true, "options": null } }
},
"site": "AZURE",
"cors": {
"allowedDomains": ["http://localhost:3000", "https://auth.mapcolonies.net"]
},
"manager": {
"enabled": true,
"url": "https://auth-manager-infra-services.apps.j1lk3njp.eastus.aroapp.io"
},
"opa": {
"enabled": true,
"servers": {
"prod": "http://opa-dev-opa-service.infra-services.svc.cluster.local:8181",
"stage": "http://opa-int-opa-service.infra-services.svc.cluster.local:8181",
"np": "http://opa-qa-opa-service.infra-services.svc.cluster.local:8181"
}
}
}
13 changes: 12 additions & 1 deletion config/test.json
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
{}
{
"manager": {
"url": "http://127.0.0.1:9"
},
"opa": {
"servers": {
"prod": "http://127.0.0.1:9",
"integration": "http://127.0.0.1:9",
"qa": "http://127.0.0.1:9"
}
}
}
Loading
Loading