Composer auth for private Magento repositories: the setup that works
Most Magento module installation guides skip the Composer authentication step. Then a developer hits a 401 Unauthorized on composer require, googles for an hour, and lands here.
This is the setup that works, written from the workflow we use on hundreds of installations.
The two-token model
Magento needs two sets of credentials configured in Composer:
- Adobe Commerce credentials for
repo.magento.com. The Adobe registry that holds Magento core packages. - Vendor-specific credentials for any third-party private registry. For example, your module supplier's registry.
You need both. The Adobe ones come from your Adobe Commerce account. Vendor ones come from the supplier with the order delivery email.
Where the credentials live
Two locations matter.
Per-project: auth.json at the store root.
{
"http-basic": {
"repo.magento.com": {
"username": "your-public-key",
"password": "your-private-key"
},
"repo.example-vendor.com": {
"username": "your-customer-id",
"password": "your-license-key"
}
}
}
Global: ~/.composer/auth.json on the developer machine. Same shape. Composer reads the project file first, then falls back to global.
For production: keep the credentials in environment variables and template auth.json at deploy time. Never commit auth.json to the repository. This sounds obvious. It is also the single most common mistake we see.
The repositories block
In composer.json under repositories, add the vendor's registry:
{
"repositories": {
"magento": {
"type": "composer",
"url": "https://repo.magento.com/"
},
"example-vendor": {
"type": "composer",
"url": "https://repo.example-vendor.com/"
}
}
}
Order matters. Composer scans repositories in declared order. If two registries provide the same package name, the first one wins.
The three failure modes
When composer require fails, the cause is almost always one of these.
1. Wrong token type. Adobe's public-key plus private-key pair goes to repo.magento.com only. If you have pasted those into the vendor's registry slot, the vendor responds with 401. Fix: re-check which credential belongs where.
2. Registry URL has changed. Vendors sometimes migrate registry domains. Your six-month-old composer.json is pointing at the old URL, which now serves a redirect Composer does not follow. Fix: check the vendor's current documentation for the registry URL.
3. Firewall blocks the registry. Production servers in restrictive network environments sometimes cannot reach repo.example-vendor.com. Fix: add the registry domain to the outbound allow-list. Some vendors offer a static IP for this.
The CI variant
In CI, the working pattern is:
COMPOSER_AUTH='{"http-basic":{"repo.magento.com":{"username":"'"$MAGENTO_USERNAME"'","password":"'"$MAGENTO_PASSWORD"'"}}}' composer install
Pass credentials via environment variables, build the JSON inline, run install. No file on disk, no risk of accidental commit.
Token rotation
Rotate the tokens quarterly. Most vendors expose a "regenerate" button in your account settings. After rotation:
- Update
~/.composer/auth.jsonon every developer machine and CI runner. - Update production secrets in the deploy pipeline.
- Run a test
composer installon each environment to confirm.
If a token leaks (accidental commit, screenshot in a bug ticket, a third party seeing the production console), rotate immediately and audit the access log if the vendor exposes one.
When this still fails
If you have checked all of the above and Composer still rejects authentication, ask the vendor to inspect their server logs for your IP. The 401 sometimes hides a 403 from a WAF rule the vendor's hosting provider added without telling them. We have debugged this exact case three times in the last year.