Installation
composer require atom/authentication-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Atom\AuthenticationBundle\AtomAuthenticationBundle::class => ['all' => true],
];
Configuration
Add the bundle’s config to config/packages/atom_authentication.yaml:
atom_authentication:
secret_key: '%env(APP_SECRET_KEY)%' # Must be a strong, unique key
header_name: 'X-Auth-Token' # Default HMAC header name
algorithm: 'sha256' # Default HMAC algorithm
First Use Case: Validating a Request
Use the AtomAuthenticationListener to validate incoming API requests:
use Atom\AuthenticationBundle\EventListener\AuthenticationListener;
// In a controller or service:
$token = $request->headers->get('X-Auth-Token');
$isValid = $this->authenticationListener->validateToken($request, $token);
Client-Side Token Generation Clients generate an HMAC token using their secret key and request data:
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'client_secret_key');
const signature = hmac.update(JSON.stringify(requestData)).digest('hex');
Server-Side Validation
In Symfony, use the AuthenticationListener to validate the token:
// In a controller or event subscriber:
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$token = $request->headers->get('X-Auth-Token');
if (!$this->authenticationListener->validateToken($request, $token)) {
throw new \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException('Invalid token');
}
}
Integration with Security Component
Extend Symfony’s security system by creating a custom Authenticator:
use Atom\AuthenticationBundle\Security\Token\HMACUserToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class HMACAuthenticator extends AbstractAuthenticator {
public function supports(Request $request) {
return $request->headers->has('X-Auth-Token');
}
public function authenticate(Request $request): TokenInterface {
$token = $request->headers->get('X-Auth-Token');
if (!$this->authenticationListener->validateToken($request, $token)) {
throw new BadCredentialsException();
}
return new HMACUserToken('anon.', []);
}
}
Secret Key Management
secret_key in config exposes it in logs or version control.%env(APP_SECRET_KEY)%) and restrict file permissions.Algorithm Mismatch
sha1 vs. sha256).Token Replay Attacks
$requestData = json_decode($request->getContent(), true);
$currentTime = time();
if (abs($requestData['timestamp'] - $currentTime) > 300) { // 5-minute window
throw new \RuntimeException('Token expired');
}
Header Injection
trusted_proxies or use a dedicated route prefix (e.g., /api/v1).Enable Verbose Logging Configure Monolog to log HMAC validation attempts:
# config/packages/monolog.yaml
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
Then log validation results in the AuthenticationListener:
$this->logger->debug('HMAC validation', ['valid' => $isValid, 'token' => $token]);
Test with curl
Simulate client requests to verify token generation/validation:
curl -X POST http://api.example.com/endpoint \
-H "X-Auth-Token: $(echo -n 'data' | openssl dgst -sha256 -hmac 'client_secret')"
Custom Token Payload
Extend the AuthenticationListener to validate additional payload fields:
public function validateToken(Request $request, string $token): bool {
$requestData = json_decode($request->getContent(), true);
return $this->validateHMAC($token, $requestData)
&& $this->validateCustomRules($requestData);
}
private function validateCustomRules(array $data): bool {
return $data['user_id'] === 'expected_value';
}
Dynamic Secret Keys Fetch secrets from a database or cache for per-user keys:
$userSecret = $this->secretService->getUserSecret($userId);
return $this->validateHMAC($token, $requestData, $userSecret);
Event-Based Extensions Dispatch events for pre/post-validation logic:
// In AuthenticationListener
$event = new PreAuthEvent($request, $token);
$this->dispatcher->dispatch($event, PreAuthEvent::NAME);
if ($event->isTokenRejected()) {
return false;
}
How can I help you explore Laravel packages today?