How CoSec Handles Authentication: JWT, OAuth2, and Custom Providers

CoSec handles authentication through a generic, type-safe Authentication<C, P> interface that transforms credentials into a CoSecPrincipal, supporting JWT tokens, OAuth2 social logins, and custom providers as injectable Spring beans.

CoSec (Context-Oriented Security) is a policy-based authorization framework for Spring applications that cleanly separates authentication from authorization. Whether you are securing reactive gateways or traditional MVC services, understanding how CoSec handles authentication allows you to integrate JWT validation, social logins, or proprietary credential schemes using the same extensible contract defined in the cosec-api module.

Core Authentication Architecture

The foundation of CoSec authentication is the Authentication<C, P> interface located in cosec-api/src/main/kotlin/me/ahoo/cosec/api/authentication/Authentication.kt. This contract is intentionally generic, allowing any credential type (C) to be verified and mapped to a principal type (P).

The Authentication Contract

All authentication providers implement the following signature:

interface Authentication<C : Credentials, out P : CoSecPrincipal> {
    val supportCredentials: Class<C>
    fun authenticate(credentials: C): Mono<out P>
}

Implementations are stateless Spring beans. The framework selects the appropriate provider by matching the supportCredentials property to the credential type extracted from the incoming request (e.g., JwtCredentials, SocialCredentials). This design allows multiple authentication strategies to coexist without coupling.

Security Context Population

Once a provider successfully authenticates the credentials, it returns a CoSecPrincipal containing the user identity and granted authorities. The principal is immediately stored in a SecurityContext that downstream components—such as the policy engine and business controllers—access to enforce authorization decisions.

Built-in Authentication Methods

CoSec provides auto-configuration for two primary authentication mechanisms, controlled via application.yml properties.

JWT Token Authentication

When you provide a signing key, CoSec automatically configures JWT validation. Enable this by setting:

cosec:
  authentication:
    jwt:
      secret: <base64-encoded-secret>
      issuer: my-app

The JwtAuthenticationProvider (located in the cosec-jwt module) implements Authentication<JwtCredentials, CoSecPrincipal>. It extracts the token from the Authorization: Bearer <token> header, validates the signature and claims, and maps the JWT payload to a CoSecPrincipal. Because its supportCredentials returns JwtCredentials::class.java, it is automatically selected for requests containing Bearer tokens.

Social OAuth2 Authentication

CoSec integrates with JustAuth to provide out-of-the-box support for Google, GitHub, and other OAuth2 providers. This is orchestrated by CoSecSocialAuthenticationAutoConfiguration in cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/authentication/social/CoSecSocialAuthenticationAutoConfiguration.kt.

Configure providers in your YAML:

cosec:
  authentication:
    social:
      registration:
        google:
          type: google
          client-id: <client-id>
          client-secret: <client-secret>
          redirect-uri: https://my-app.com/oauth/callback/google
        github:
          type: github
          client-id: <client-id>
          client-secret: <client-secret>

The flow works as follows:

  1. Auto-configuration creates a JustAuthProvider for each registration and registers them with SocialProviderManager.
  2. Login request redirects to /oauth/authorize/{provider}, returning a SocialCredentials object containing the authorization code.
  3. Authentication is handled by SocialAuthentication in cosec-social/src/main/kotlin/me/ahoo/cosec/social/SocialAuthentication.kt, which exchanges the code for an access token and fetches the user profile.
  4. Principal conversion occurs via SocialUserPrincipalConverter, transforming the SocialUser into a CoSecPrincipal for the security context.

Implementing Custom Authentication Providers

For non-standard requirements—such as API keys or LDAP—you can implement the Authentication interface directly. CoSec will automatically detect and register your bean.

@Component
class ApiKeyAuthenticationProvider : Authentication<ApiKeyCredentials, CoSecPrincipal> {
    override val supportCredentials = ApiKeyCredentials::class.java

    override fun authenticate(credentials: ApiKeyCredentials): Mono<CoSecPrincipal> {
        // Validate against database or cache
        return if (isValidKey(credentials.key)) {
            Mono.just(CoSecPrincipal(
                id = credentials.key,
                roles = setOf("API_USER")
            ))
        } else {
            Mono.error(InvalidCredentialsException("Invalid API Key"))
        }
    }
}

No additional configuration is required; Spring’s component scanning injects your provider into the authentication chain.

Request Flow and Filter Integration

Authentication executes early in the request lifecycle, before the policy engine evaluates authorization rules. In reactive applications, ReactiveAuthorizationFilter (cosec-webflux/src/main/kotlin/me/ahoo/cosec/webflux/ReactiveAuthorizationFilter.kt) intercepts the request. For gateway scenarios, AuthorizationGatewayFilter (cosec-gateway/src/main/kotlin/me/ahoo/cosec/gateway/AuthorizationGatewayFilter.kt) performs the same role.

These filters:

  1. Extract credentials from headers or request parameters.
  2. Locate the matching Authentication bean via supportCredentials.
  3. Execute authenticate() and populate the SecurityContext.
  4. Proceed to authorization only if authentication succeeds.

Your controllers then access the principal directly:

@RestController
@RequestMapping("/api")
class ProfileController {

    @GetMapping("/profile")
    suspend fun getProfile(context: ReactiveSecurityContext): ProfileDto {
        val principal = context.authentication.principal as CoSecPrincipal
        return profileService.getById(principal.id)
    }
}

Summary

  • Pluggable Interface: All authentication flows implement Authentication<C, P> in cosec-api/src/main/kotlin/me/ahoo/cosec/api/authentication/Authentication.kt, enabling type-safe credential handling.
  • Auto-configuration: JWT and OAuth2 providers are instantiated automatically based on property files, with logic centralized in CoSecSocialAuthenticationAutoConfiguration.kt.
  • Social Login: JustAuth integration via SocialAuthentication.kt handles OAuth2 flows and converts social profiles to CoSecPrincipal objects.
  • Custom Extensions: Implement the interface as a Spring component to add API-key, LDAP, or proprietary authentication without modifying core code.
  • Filter Chain: AuthorizationGatewayFilter and ReactiveAuthorizationFilter trigger authentication before policy evaluation, ensuring the security context is populated for downstream authorization.

Frequently Asked Questions

How do I enable JWT authentication in CoSec?

Enable JWT authentication by adding a signing key to your application.yml under cosec.authentication.jwt.secret. The auto-configuration in the cosec-jwt module then creates a JwtAuthenticationProvider bean that validates Authorization: Bearer headers and maps claims to a CoSecPrincipal.

Can I use multiple authentication methods at the same time?

Yes. CoSec supports simultaneous authentication strategies because each provider declares its supported credential type via the supportCredentials property. The framework selects the appropriate Authentication bean based on the credential type present in the request, allowing JWT and social login to coexist.

How does CoSec convert a social login to an internal principal?

The SocialAuthentication class delegates to a SocialUserPrincipalConverter (default: DirectSocialUserPrincipalConverter). This converter transforms the SocialUser object returned by JustAuth into a CoSecPrincipal containing the user ID and roles, which is then stored in the security context for authorization checks.

Where does authentication occur in the CoSec request pipeline?

Authentication occurs in the filter layer before authorization. AuthorizationGatewayFilter (for gateways) or ReactiveAuthorizationFilter (for WebFlux applications) invokes the configured Authentication bean immediately after credential extraction. Only after successful authentication does the request proceed to the policy engine for authorization decisions.

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 →