Skip to content

feat(authz): integrate OPA fine-grained authorization middleware#94

Merged
Miracle656 merged 1 commit into
Miracle656:mainfrom
Depo-dev:feat/opa-authz-86
May 30, 2026
Merged

feat(authz): integrate OPA fine-grained authorization middleware#94
Miracle656 merged 1 commit into
Miracle656:mainfrom
Depo-dev:feat/opa-authz-86

Conversation

@Depo-dev
Copy link
Copy Markdown
Contributor

Summary

Integrates Open Policy Agent (OPA) for fine-grained API authorization (Issue #86).

  • policies/authz.rego: Rego rules covering public paths (no token required), bearer-token-gated routes, admin-only prefixes (/admin, /internal), and a rate-limit guard. Each deny path emits deny_reason + deny_rule for structured audit logs.
  • policies/authz_test.rego: OPA unit tests for all allow/deny branches.
  • src/middleware/opa.ts: Express middleware factory (createOpaMiddleware) that POSTs request context (path, method, token, role, user) to the OPA /v1/data endpoint. Returns 403 with rule + reason on denial; 503 and fails closed when OPA is unreachable.
  • src/tests/opa.test.ts: Jest tests covering allow, deny (with rule + reason body), 503-on-OPA-unavailable, and custom extractor paths.

Acceptance criteria

  • Every request evaluated against policy engine (middleware runs before route handlers)
  • Denials logged with rule context (console.warn includes rule, reason, path, method, user, role, ip)
  • Authorization enforced before handler execution (middleware positioned before routes)

Test plan

  • Run npm test -- src/tests/opa.test.ts to confirm 9 unit tests pass
  • Start OPA server: opa run --server policies/
  • Make unauthenticated request to /transfers/... and confirm 403 with deny_rule: require_bearer_token
  • Make request with Bearer token and confirm 200
  • Make request to /admin/... without admin role and confirm 403 with deny_rule: require_admin_role
  • Stop OPA server and confirm 503 Authorization service unavailable

Closes #86

…acle656#86)

Adds Open Policy Agent authorization evaluated on every request
before handlers execute, with structured denial logging.

- policies/authz.rego: Rego rules covering public paths (no token
  required), bearer-token-gated routes, admin-only prefixes, and
  rate-limit guard; each deny path emits a deny_reason + deny_rule
  for audit logs
- policies/authz_test.rego: OPA unit tests for all allow/deny
  branches
- src/middleware/opa.ts: Express middleware factory (createOpaMiddleware)
  that POSTs request context (path, method, token, role, user) to the
  OPA /v1/data endpoint; returns 403 with rule+reason on denial, 503
  on OPA unreachable (fail-closed)
- src/__tests__/opa.test.ts: Jest tests covering allow, deny,
  503-on-unavailable, and custom extractor paths
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 30, 2026

@Depo-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Copy link
Copy Markdown
Owner

@Miracle656 Miracle656 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good shape — the middleware + policy + tests are all real:

  • authz.rego: default-deny baseline (default allow := false), public-path allow set, bearer-token gate, admin-prefix gate, rate-limit guard. Every deny path emits both deny_reason and deny_rule so logs are structured rather than free-text.
  • authz_test.rego: 11 Rego tests covering public paths, authed routes (with/without token), admin paths (with/without role), and the deny-reason/rule values themselves. Good coverage.
  • createOpaMiddleware: clean factory, POSTs { input: {...} } to /v1/data/<package>, returns 403 with { error, rule, reason } JSON body on deny. Closed-fail to 503 when OPA is unreachable — the right default for an authz dependency.
  • opa.test.ts: mocks http.request at the constructor level, exercises allow/deny/503 paths plus the custom getRole/getUser extractors.

One note (not blocking the merge)

The middleware is available but not enforcedsrc/api.ts isn't modified to actually app.use(createOpaMiddleware()). The PR description claims "Every request evaluated against policy engine" as a satisfied criterion; that's slightly aspirational since the middleware is sitting in the codebase unwired.

That's actually the right call for a first landing — flipping default-deny on all consumers in the same commit would break every client not yet issuing bearer tokens. The follow-up to wire it in needs a coordinated rollout (a WRAITH_OPA_ENFORCE=true env-var gate, then flip after consumers migrate). Worth filing as a tracking issue when you decide to roll out.

Merging. Closes #86.

@Miracle656 Miracle656 merged commit 1781412 into Miracle656:main May 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OPA authorization policies

2 participants