What Is CoSec and What Problem Does It Solve? A Guide to the Reactive Security Framework
CoSec is a reactive, multi-tenant security framework that combines RBAC and policy-based access control to solve the challenge of declaring fine-grained, conditional permissions across multiple tenants without blocking your application threads.
CoSec (short for Co‑Secured) is an open-source authorization engine written in Kotlin and maintained in the ahoo-wang/cosec repository. Unlike traditional security libraries that lock you into a single access model or framework, CoSec provides a declarative, JSON-driven policy engine that integrates with both Spring WebFlux and Spring MVC while remaining fully non-blocking.
The Five Core Problems CoSec Solves
Traditional security frameworks often force you to choose between role-based access control (RBAC) and attribute-based access control (ABAC), or they tightly couple authorization logic to a specific web framework. CoSec addresses these limitations through a unified, extensible architecture.
Multi-Tenant Isolation
In SaaS applications, each tenant requires isolated sets of roles, permissions, and policies. CoSec scopes all policies by tenantId and loads them per-tenant via the PolicyRepository interface. According to the source code in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/LocalPolicyLoader.kt, policies can be loaded from the classpath, database, or any external source, ensuring tenant-specific configurations remain segregated.
Fine-Grained, Conditional Access
Modern applications require dynamic rules such as "allow only if the request originates from a specific IP range and the user owns the resource." CoSec provides a condition matcher DSL that supports predicates like authenticated, rateLimiter, path, and regular directly in JSON policy definitions. This allows complex authorization logic without writing custom code for every rule.
Scalable Reactive Processing
Blocking security checks create bottlenecks in reactive architectures. All core CoSec APIs return Mono and Flux types, as seen in cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt. The authorize() method signature returns Mono<AuthorizeResult>, ensuring that permission checks never block the event loop in high-throughput WebFlux applications.
Extensibility Without Core Modifications
When business rules require custom matching logic—such as validating against an external fraud detection service—you should not need to fork the framework. CoSec uses a Service Provider Interface (SPI) pattern. You can implement ActionMatcherFactory or ConditionMatcherFactory and register your extensions via META-INF/services/ to add custom matchers without touching the core codebase.
Built-In Observability
Security decisions must be auditable and traceable. The cosec-opentelemetry module provides TracingAuthorization, which decorates any Authorization implementation to automatically create OpenTelemetry spans named cosec.authorize. This integration, located in cosec-opentelemetry/src/main/kotlin/me/ahoo/cosec/opentelemetry/TracingAuthorization.kt, captures latency and decision outcomes for every authorization check.
How CoSec Works: Architectural Overview
The CoSec authorization flow follows a pipeline architecture that transforms HTTP requests into authorization decisions through five distinct stages.
Policy Loading and Storage
Policies are loaded via PolicyRepository implementations. The LocalPolicyLoader utility in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/LocalPolicyLoader.kt demonstrates loading JSON policies from the classpath:
val policies = LocalPolicyLoader.load("cosec-policy.json")
These policies conform to the JSON schema defined in schema/cosec-policy.schema.json, enabling IDE auto-completion and validation.
Request Parsing and Security Context
The RequestParser interface, defined in cosec-core/src/main/kotlin/me/ahoo/coosec/context/request/RequestParser.kt, converts HTTP requests into a unified Request model. For servlet environments, ServletRequestParser in cosec-webmvc/src/main/kotlin/me/ahoo/cosec/servlet/ServletRequestParser.kt handles the conversion, while reactive applications use filters in cosec-webflux.
The SecurityContextHolder manages the authenticated principal (CoSecPrincipal) and runtime attributes through the SecurityContext class located in cosec-core/src/main/kotlin/me/ahoo/cosec/context/SecurityContext.kt.
The Authorization Engine
The SimpleAuthorization class in cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt implements the decision logic through a three-tier evaluation:
- Global policies – Applied to all tenants universally
- Principal-specific policies – Attached directly to the authenticated user
- App-role permissions – Role-based permissions scoped by application or space
The engine short-circuits on the first explicit deny, returns the first allow if found, or falls back to implicit deny if no rules match.
Result Propagation and Serialization
After evaluation, the AuthorizeResult object (containing authorized: Boolean and optional errorCode) is serialized by CoSecJsonSerializer in cosec-core/src/main/kotlin/me/ahoo/cosec/serialization/CoSecJsonSerializer.kt and written to the HTTP response.
OpenTelemetry Integration
The TracingAuthorization wrapper automatically instruments the authorization flow, creating spans that track the decision process without requiring manual instrumentation in your business code.
Implementing CoSec in a Spring WebFlux Application
To integrate CoSec into a Spring Boot application, add the starter dependencies and configure the policy repository.
build.gradle.kts:
implementation("me.ahoo.cosec:cosec-core:{{latest-version}}")
implementation("me.ahoo.cosec:cosec-spring-boot-starter:{{latest-version}}")
SecurityConfig.kt:
@Configuration
class SecurityConfig {
@Bean
fun policyRepository(): PolicyRepository = LocalPolicyRepository(
policyPath = "cosec-policy.json"
)
@Bean
fun authorization(
policyRepo: PolicyRepository,
appRoleRepo: AppRolePermissionRepository,
blacklist: BlacklistChecker = BlacklistChecker.NoOp
): Authorization = SimpleAuthorization(policyRepo, appRoleRepo, blacklist)
}
cosec-policy.json:
{
"id": "global-allow-all",
"type": "global",
"effect": "allow",
"condition": { "bool": { "must": [] } },
"statements": [{ "action": "*", "effect": "allow" }]
}
The ReactiveSecurityFilter in cosec-webflux/src/main/kotlin/me/ahoo/cosec/webflux/ReactiveSecurityFilter.kt automatically intercepts requests, parses them into Request objects, retrieves the SecurityContext, and invokes authorization.authorize(request, context), returning a Mono<AuthorizeResult> that determines whether the request proceeds.
Extending the Engine with SPI
CoSec's extensibility relies on factory patterns registered through Java's ServiceLoader mechanism.
Custom Action Matchers
To implement path matching or custom action identification, create a class implementing ActionMatcherFactory. Reference the PathActionMatcher in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/action/PathActionMatcher.kt as an example of matching request paths using Ant-style patterns. Register your implementation in META-INF/services/me.ahoo.cosec.policy.action.ActionMatcherFactory.
Custom Condition Matchers
For attribute-based checks, implement ConditionMatcherFactory. The ContainsConditionMatcher in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/ContainsConditionMatcher.kt demonstrates checking whether a request attribute contains a configured value. This approach allows you to inject business-specific logic—such as subscription tier validation—directly into the policy evaluation pipeline.
Summary
- CoSec is a Kotlin-based, reactive security framework providing RBAC and policy-based access control for multi-tenant applications.
- It solves multi-tenant isolation by scoping policies to
tenantIdand loading them through thePolicyRepositoryinterface. - The framework supports fine-grained authorization through a JSON condition DSL without requiring custom code deployments.
- All authorization APIs return
Mono/Flux, ensuring non-blocking execution compatible with Spring WebFlux. - Extensibility is achieved through SPI-based factories (
ActionMatcherFactory,ConditionMatcherFactory) that allow custom logic without modifying core source files. - Observability is built-in via OpenTelemetry integration in the
cosec-opentelemetrymodule, automatically tracing authorization decisions.
Frequently Asked Questions
What is CoSec and how does it differ from Spring Security?
CoSec is a policy-driven authorization framework that focuses specifically on declarative, JSON-based access control with native multi-tenant support. While Spring Security provides comprehensive authentication and authorization, CoSec excels at complex, tenant-scoped policies and reactive processing without blocking. You can use CoSec alongside Spring Security or as a specialized authorization layer within it.
Is CoSec suitable for servlet-based applications or only reactive?
CoSec supports both architectures. The cosec-webmvc module provides ServletRequestParser for traditional servlet environments, while cosec-webflux offers ReactiveSecurityFilter for non-blocking applications. The core authorization engine in SimpleAuthorization.kt remains the same regardless of the transport layer.
How does CoSec handle multi-tenant authorization?
CoSec handles multi-tenancy by scoping policies to a tenantId field within the policy definition. The PolicyRepository loads tenant-specific policies, and the authorization engine evaluates them in the context of the current tenant. This ensures that Tenant A's admin permissions never apply to Tenant B's resources, even when both tenants exist in the same database instance.
Can I use CoSec without Spring Boot?
Yes. While CoSec provides a convenient Spring Boot starter in cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/CoSecAutoConfiguration.kt, the core modules (cosec-core) have no Spring dependencies. You can instantiate SimpleAuthorization directly and integrate it with any framework or standalone application by implementing the RequestParser and SecurityContextHolder interfaces for your specific environment.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →