Project - Reusable Authentication Infrastructure
Designing and building a Symfony bundle that unifies JWT authentication across multiple services, replacing scattered legacy code with configuration-driven context routing and role mapping.
- Client
- Solcon
- Year
- Service
- Backend Development, Security Architecture
Overview
At Solcon, backend services receive tokens from multiple authorization servers — each with different signing keys, claim structures, and role conventions. Previously, every application maintained its own authentication logic: custom user classes, multiple authenticators, dedicated factories, and hardcoded or reverse-engineered role assignments. This was a legacy of the old Guard system from Symfony 5 and earlier, and it meant duplicated, fragile code in every service.
I designed and built a reusable Symfony bundle that decorates Symfony's built-in access token authenticators — OAuth, OpenID Connect, and CAS — into a single configurable layer. The bundle peeks inside each incoming token, determines which validation context applies, maps custom claims to Symfony roles, and produces a generic Identity — all driven by YAML. The result is one authenticator and one user model per service, replacing all the old per-application auth code.
Key Contributions
Context-Based Authentication
The central problem is that a single service can receive tokens from different authorization servers. Rather than writing conditional logic per application to figure out what kind of token arrived, the bundle matches each token against configured contexts using an expression language. For example, a context can match on client type, audience, or the presence of specific claims — and assign the appropriate Symfony roles automatically.
This means adding support for a new type of token is a configuration change, not a code change. No new factories, no new user classes, no conditionals — just a new context entry with an expression and the roles it should grant.
Replacing Legacy Authentication Code
The bundle was designed specifically to replace the Guard-based authentication code that had accumulated across services since Symfony 5. Each service had its own approach to reading tokens, determining user types, and assigning roles — often with duplicated or hardcoded logic. The bundle consolidates all of this into a shared, tested library where authentication behavior is defined declaratively.
Extensible Handler Architecture
The bundle ships with built-in handlers for signed tokens, OpenID Connect, OAuth, and CAS, but is designed to be extended. New handler types can be added through a factory pattern without modifying the bundle itself. Applications can also override specific parts of the validation pipeline when needed, or plug in their own handler as a service.
Flexible Key Management
To support different authorization servers, the bundle handles a range of key formats and sources — from static key files to keys fetched dynamically from discovery endpoints with caching. Custom environment variable processors simplify loading RSA keys from files, avoiding the need for additional bundle dependencies.
Extending the Authentication Process
The bundle provides hooks that allow applications to enrich the authentication process through event listeners — for example, fetching additional user attributes from claims or adding conditional roles based on business logic. This keeps the core authenticator generic while letting each service customize behavior where needed.
- PHP
- Symfony
- JWT
- Jose
- Auth Setup
- Config-Only
- Token Routing
- Multi-Context
- Per-App Auth Code
- Zero
- Legacy Replaced
- Guard