macpaw/doctrine-aws-iam-rds-auth-bundle
Symfony bundle for Doctrine DBAL that enables Amazon RDS connections using AWS IAM authentication. It generates short-lived IAM auth tokens instead of static database passwords, helping PHP applications manage database credentials more securely.
This bundle does one job: mint a short-lived AWS RDS IAM authentication token and hand it to Doctrine DBAL as the connection password. In a Laravel app you will rarely use the bundle half (it registers a Symfony Bundle class and a DI extension); you will use the token provider class from src/ directly and wire it yourself. Read the token provider first, then the service definition in Resources/config/ to see exactly which DBAL params it overrides.
composer require macpaw/doctrine-aws-iam-rds-auth-bundle — v1.1.0 requires doctrine/dbal ^3.0 || ^4.0, so Laravel 9+ (which ships with DBAL 3) is fine, older DBAL 2 stacks are not.aws sts get-caller-identity inside the container, or check AWS_ROLE_ARN / AWS_WEB_IDENTITY_TOKEN_FILE for IRSA.rds-db:connect on arn:aws:rds-db:<region>:<account>:dbuser:<DbiResourceId>/<dbuser> — note it is the resource id, not the DB identifier.CREATE USER app IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; on MySQL/Aurora MySQL) and require TLS.use Doctrine\DBAL\DriverManager;
$config = [
'dbname' => config('database.connections.aurora.database'),
'host' => config('database.connections.aurora.host'),
'port' => 3306,
'user' => 'app',
'password' => app(TokenProvider::class)->getToken($host, $port, 'app', $region),
'driver' => 'pdo_mysql',
'sslmode' => 'require',
];
$conn = DriverManager::getConnection($config);
Bind the provider as a singleton. The token generator is stateless apart from the AWS credentials/client; bind it once in a service provider so the AWS SDK client is reused across jobs.
Token cache keyed by (host, port, user, region). Tokens are valid for 15 minutes and are signed for a specific endpoint and user. Cache in-memory for the request/job lifetime with a safety margin (regenerate at ~12 minutes); never persist them to Redis or the database.
Connection factory, not a global config entry. Because the password expires, produce a fresh Doctrine\DBAL\Connection per unit of work rather than holding one in the container:
class AuroraConnectionFactory
{
public function __construct(private TokenProvider $tokens) {}
public function make(): Connection
{
return DriverManager::getConnection($this->params());
}
}
Long-running workers (Horizon, queue:work). A DBAL connection opened at boot will die at the token's 15-minute mark on the next new underlying connection, and idle connections get killed by wait_timeout anyway. Rebuild the connection per job, or call DB::purge() on a schedule; do not rely on reconnect-with-old-password.
DBAL 3 vs DBAL 4. v1.1.0 adds DBAL 4 support, which matters if you are on Laravel 11/12 with a newer doctrine/dbal. Driver class names, DriverManager params, and the middleware API changed between majors (Doctrine\DBAL\Driver\Middleware still exists in 4, but several AbstractDriverMiddleware helpers moved). If you wrap this package in your own middleware, test on both majors before bumping.
Middleware pattern. If you want transparent token injection, register a DBAL middleware that mutates $params['password'] on connect(). This is the cleanest seam for Laravel because it keeps config/database.php as the single source of truth.
Testing. Type-hint the package's token provider interface and swap it in tests with a fake returning a fixed string, so your suite never calls AWS. Keep the real factory behind a feature flag and hit a local MariaDB/MySQL without SSL in testing.
Multiple databases. One token per connection tuple. Do not reuse the primary's token for a read replica endpoint — the host is part of the signed request.
vendor:publish, no Laravel auto-discovery of its config tree. Treat it as a library: instantiate the provider with an explicit config array and ignore the DependencyInjection/ folder.v1.0.1...v1.1.0 before rolling this into production; the practical change you can rely on is the DBAL 3/4 constraint widening.AWS_REGION/AWS_DEFAULT_REGION in the container.sslmode=require (Postgres) or a CA option (PDO::MYSQL_ATTR_SSL_CA) the server returns access-denied rather than a TLS error.connect() time inside the driver. Probe credentials once at boot in a health check.password from query/connection logging.1.1.*, keep an escape hatch (a five-line Rds\AuthTokenGenerator call with the AWS SDK) so you can drop the dependency if it stalls.SQLSTATE[28000] (auth token rejected — expired, wrong resource id, missing IAM permission) from AWS SDK credential exceptions (no credentials, wrong region) from SQLSTATE[HY000] (network/SG). Catching all three as "DB error" will cost you hours.How can I help you explore Laravel packages today?