How CoSec Policy-Based Authorization Works: Architecture and Implementation Guide

CoSec implements policy-based authorization by evaluating a hierarchy of policies, statements, conditions, and role-permissions against incoming requests using a declarative JSON/YAML configuration model.

CoSec is an open-source security framework hosted at ahoo-wang/cosec that provides a comprehensive policy-based authorization system for JVM applications. The engine operates entirely on declarative policies that define access rules through JSON or YAML files, allowing developers to externalize authorization logic from application code.

Policy Model Architecture

The CoSec authorization framework is built on four core components that form a hierarchical evaluation structure.

Policy Component

A Policy serves as the top-level container holding an identifier, a list of statements, and a condition that determines whether the policy applies to a specific request. Defined in [Policy.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/Policy.kt), this component represents the entry point for all authorization decisions.

Statement and Effect

Each policy contains Statements that specify concrete access rules. According to [Statement.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/Statement.kt), every statement includes:

  • A descriptive name
  • An effect value of either ALLOW or DENY
  • A verify function that checks request-principal attributes

Condition Matching

The Condition interface in [Condition.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/condition/Condition.kt) provides reusable matchers for URL patterns, HTTP methods, IP addresses, and other request attributes. Conditions filter which policies and permissions participate in the evaluation for a given request.

Role-Based Permissions

AppRolePermission, defined in [AppRolePermission.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/permission/AppRolePermission.kt), maps role identifiers to permission sets. When a principal carries role identifiers, the system retrieves corresponding permissions and evaluates them as statements with effects.

Authorization Verification Flow

The core authorization algorithm resides in [SimpleAuthorization.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt) and follows a strict evaluation sequence:

class SimpleAuthorization(
    private val policyRepository: PolicyRepository,
    private val appRolePermissionRepository: AppRolePermissionRepository,
    private val blacklistChecker: BlacklistChecker = BlacklistChecker.NoOp
) : Authorization { … }

Root User Shortcut

If the principal represents the root user, the request is automatically allowed without further evaluation (lines 52-58). This provides a super-admin bypass for emergency access scenarios.

Blacklist Verification

Before policy evaluation, the system invokes a configurable BlacklistChecker that can reject requests based on predefined blocklists (lines 14-22). This check occurs at the entry point to prevent unnecessary processing of banned entities.

Policy Retrieval and Matching

The engine loads policies through three channels:

  1. Global policies via policyRepository.getGlobalPolicy() (lines 63-68)
  2. Principal-specific policies from principal.policies (lines 70-77)
  3. App-role permissions fetched via appRolePermissionRepository when the principal carries roles (lines 80-88)

Each candidate policy's condition is evaluated first through policy.condition.match(...). Only policies with matching conditions proceed to statement inspection (lines 52-54).

Statement Evaluation Logic

The verification engine applies explicit deny precedence:

  • DENY statements are examined first. The first explicit deny (VerifyResult.EXPLICIT_DENY) short-circuits evaluation immediately, returning a PolicyVerifyContext (lines 56-71).
  • ALLOW statements are processed next. The first explicit allow (VerifyResult.ALLOW) yields a successful PolicyVerifyContext (lines 75-90).

Result Conversion

The VerifyResult contained in the VerifyContext transforms into an AuthorizeResult of either ALLOW, EXPLICIT_DENY, or IMPLICIT_DENY (lines 98-106). If no policy or permission matches, the request receives an implicit deny.

Policy Storage and Caching

CoSec optimizes policy retrieval through a multi-layered caching architecture backed by Redis.

Cache Configuration

CoSecPolicyCacheAutoConfiguration wires a Redis-backed PolicyCache and global-policy-index cache to ensure repeated lookups remain performant. This auto-configuration resides in [CoSecPolicyCacheAutoConfiguration.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/authorization/cache/CoSecPolicyCacheAutoConfiguration.kt).

Redis Repository Implementations

OpenAPI Policy Generation

CoSec supports automatic policy generation from OpenAPI specifications, eliminating manual JSON authoring.

The [OpenAPIPolicyGenerator.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-openapi/src/main/kotlin/me/ahoo/cosec/openapi/generator/OpenAPIPolicyGenerator.kt) class provides:

val policy = OpenAPIPolicyGenerator.generate(openAPI)

This generates a complete policy containing statements for each API operation, deriving path patterns and HTTP methods directly from the specification.

Implementation Examples

Defining a Declarative Policy

Store JSON policy definitions under src/main/resources/cosec-policy/:

{
  "id": "order-service-policy",
  "category": "APPLICATION",
  "name": "Order Service Policy",
  "description": "Controls access to order endpoints",
  "condition": { "pathPattern": "/orders/**" },
  "statements": [
    {
      "name": "allow-read",
      "effect": "ALLOW",
      "condition": { "httpMethod": "GET" },
      "resource": "order:read"
    },
    {
      "name": "deny-delete",
      "effect": "DENY",
      "condition": { "httpMethod": "DELETE" },
      "resource": "order:delete"
    }
  ]
}

LocalPolicyLoader automatically loads these files at startup.

Spring Boot Integration

Enable authorization with the @EnableCoSecAuthorization annotation:

@SpringBootApplication
@EnableCoSecAuthorization
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

This annotation imports CoSecAuthorizationAutoConfiguration, which registers the SimpleAuthorization bean.

Programmatic Authorization Checks

Inject the Authorization interface and verify requests programmatically:

val request = DefaultRequest(
    method = HttpMethod.POST,
    url = "/orders/123",
    appId = "order-service",
    spaceId = "default",
    headers = HttpHeaders().apply { 
        add("Authorization", "Bearer abc") 
    }
)

val principal = CoSecPrincipal(
    id = "user-42",
    roles = setOf("buyer"),
    policies = emptySet(),
    attributes = emptyMap()
)

val context = SecurityContext(principal = principal)

authorization.authorize(request, context)
    .subscribe { result ->
        println("Authorization result: $result")
    }

The authorize() method returns AuthorizeResult.ALLOW, EXPLICIT_DENY, or IMPLICIT_DENY.

Generating Policies from OpenAPI

val openAPI = OpenAPIParser()
    .readLocation("petstore.yaml", null, ParseOptions())
val generatedPolicy = OpenAPIPolicyGenerator.generate(openAPI)
policyRepository.save(generatedPolicy)

Summary

  • CoSec policy-based authorization evaluates requests against hierarchical policies containing statements with explicit ALLOW or DENY effects.
  • The [SimpleAuthorization.kt](https://github.com/ahoo-wang/cosec/blob/main/cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt) engine applies explicit deny precedence, checking root access, blacklists, global policies, principal policies, and role-permissions in sequence.
  • Conditions filter applicable policies using matchers for paths, methods, and attributes before statement evaluation occurs.
  • Redis-backed caching through CoSecPolicyCacheAutoConfiguration ensures high-performance policy retrieval in production environments.
  • OpenAPI integration enables automatic policy generation from API specifications, reducing manual configuration overhead.

Frequently Asked Questions

How does CoSec handle conflicting allow and deny statements?

CoSec evaluates DENY statements before ALLOW statements. If any statement produces VerifyResult.EXPLICIT_DENY, the evaluation short-circuits immediately and returns a denial. Only if no deny matches does the engine check for explicit allows. This deny-override logic ensures restrictive access control as implemented in lines 56-90 of SimpleAuthorization.kt.

Can CoSec policies be loaded from sources other than JSON files?

Yes. While LocalPolicyLoader handles classpath JSON/YAML files, the PolicyRepository interface abstracts storage. The RedisPolicyRepository implementation enables dynamic policy management through Redis, and custom implementations can integrate with databases or configuration services.

What is the difference between implicit deny and explicit deny in CoSec?

An explicit deny occurs when a matching policy statement with effect: DENY validates against the request. An implicit deny occurs when no policies or permissions match the request at all. Explicit denies are logged with context via PolicyVerifyContext, while implicit denies represent default secure behavior when no authorization rules apply.

How does CoSec optimize policy evaluation performance?

CoSec employs a multi-layer caching strategy configured through CoSecPolicyCacheAutoConfiguration. Global policies and principal-specific policies are cached in Redis-backed caches to prevent repeated repository lookups. Additionally, condition matching occurs before statement evaluation, ensuring expensive permission checks only run for relevant policies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →