Magento 2 REST API Authentication: Admin, Customer, Integration
Every request to the Magento 2 REST API runs as someone. It might be an admin user, a customer, an integration or a guest, and that identity decides which routes the request may call. Choosing the wrong method is common. A typical example is an ERP connector that logs in as a real admin user, with that user's password stored in plain text in another system. This article explains the options in Magento Open Source and Adobe Commerce 2.4.8 and when each one fits.
The four ways to authenticate
| Method | Who it represents | Typical use |
|---|---|---|
| Admin token | An admin user | Short lived scripts, manual testing |
| Customer token | A storefront customer | Mobile apps, custom frontends |
| Integration | A named integration with its own ACL | ERPs, PIMs, marketplaces, any system to system connection |
| Session | The admin or customer logged in through the browser | JavaScript on Magento's own pages calling REST |
Routes declare what they accept through the <resources> node in webapi.xml. An ACL resource such as Magento_Catalog::products requires an admin or integration with that permission. The special resource self requires a customer, and anonymous accepts anyone. Building such routes is covered in the custom REST endpoint guide.
Admin tokens
An admin token is requested with a username and password:
curl -s -X POST https://example.test/rest/V1/integration/admin/token \
-H "Content-Type: application/json" \
-d '{"username": "api.user", "password": "secret"}'
The response is a token string. Send it on later requests as Authorization: Bearer <token>. Since 2.4.4 these tokens are JWTs by default.
In 2.4.x, Magento_TwoFactorAuth is enabled out of the box and it changes this flow. With 2FA active, the plain admin token endpoint refuses to issue a token and you have to use a provider specific endpoint instead, for example for Google Authenticator:
curl -s -X POST https://example.test/rest/V1/tfa/provider/google/authenticate \
-H "Content-Type: application/json" \
-d '{"username": "api.user", "password": "secret", "otp": "123456"}'
That works for a person running a script. It does not work for an unattended system, because nobody is there to type the code. This is the main reason machine to machine connections should use integrations and not admin accounts. More about the 2FA module in the admin two factor authentication article.
Admin tokens expire. The lifetime is set in Stores > Configuration > Services > OAuth > Access Token Expiration, with a default of 4 hours for admins. The token has the same ACL permissions as the admin user's role, so a token from a full administrator can do anything the API allows.
Customer tokens
Customer tokens work the same way with a different endpoint:
curl -s -X POST https://example.test/rest/V1/integration/customer/token \
-H "Content-Type: application/json" \
-d '{"username": "[email protected]", "password": "secret"}'
curl -s -H "Authorization: Bearer $CUSTOMER_TOKEN" \
https://example.test/rest/V1/customers/me
A customer token can only call routes with the self or anonymous resource. The default lifetime is 1 hour, set in the same configuration section. Storefronts that need longer sessions should refresh the token by logging in again or keep the session elsewhere. Do not just raise the lifetime to weeks.
From the admin, the customer edit page has a Force Sign-In button that revokes the customer's tokens. That is useful when an account is compromised. For headless storefronts, GraphQL has its own token mutations. They are covered in the GraphQL customer authentication article.
The token endpoints are protected by authentication locks. After a configurable number of failed attempts the account is locked for a while. The settings are in the OAuth configuration section under Authentication Locks. Keep them enabled, because the token endpoints are a natural target for password guessing.
Integrations
An integration is the right tool for any external system. You create it in System > Extensions > Integrations, give it a name and an email, and pick the exact ACL resources it needs on the API tab. When you activate it, Magento generates four values: consumer key, consumer secret, access token and access token secret.
Benefits over an admin account:
- Permissions are limited to what the integration needs. A stock sync does not need access to customers.
- It is not tied to a person. Staff changes and 2FA do not break it.
- Tokens do not expire, and you can rotate them with Reauthorize when a secret leaks.
- Logs and audits show a named integration, not a shared admin login.
OAuth signing vs bearer tokens
The intended way to use integration credentials is OAuth 1.0a: each request is signed with the consumer key, consumer secret, access token and access token secret. Most HTTP client libraries have an OAuth 1.0a plugin, and tools like Postman support it directly.
Since 2.4.4, using the integration access token alone as a bearer token is disabled by default. Many older connectors rely on that, and they start returning 401 errors after an upgrade. You can turn it back on in Stores > Configuration > Services > OAuth > Consumer Settings with the option that allows OAuth access tokens to be used as standalone bearer tokens, or from the CLI:
bin/magento config:set oauth/consumer/enable_integration_as_bearer 1
bin/magento cache:clean config
It is a trade off. A bearer token is simpler to use but anyone who sees it can replay it. Signed requests carry a timestamp and a nonce, so a captured request cannot simply be sent again later. If the external system supports OAuth 1.0a, use it and leave the setting off. If you enable it, make sure the token only travels over HTTPS and is stored as a secret on the other side.
Integrations in code
A module can also declare an integration in etc/integration/config.xml and its ACL in etc/integration/api.xml. Magento creates it during setup:upgrade, and an admin still has to activate it to generate tokens. This is useful when you ship a connector module and want the integration and its permissions to be versioned with the code.
Session authentication
When JavaScript on Magento's own storefront or admin calls the REST API, the request can use the existing session cookie. This is how some core checkout and customer features talk to REST. It only makes sense for code running on the same domain inside Magento pages. External clients should never try to reuse session cookies.
Knowing who is calling inside your code
In a service method behind a REST route, inject Magento\Authorization\Model\UserContextInterface to find out who made the request:
<?php
declare(strict_types=1);
namespace Acme\Notice\Model;
use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\Exception\AuthorizationException;
class CallerGuard
{
public function __construct(
private readonly UserContextInterface $userContext
) {
}
public function requireCustomerId(): int
{
if ($this->userContext->getUserType() !== UserContextInterface::USER_TYPE_CUSTOMER) {
throw new AuthorizationException(__('This action is only available to customers.'));
}
return (int) $this->userContext->getUserId();
}
}
The user type constants cover integrations, admins, customers and guests. For customer routes, prefer the self resource with a forced %customer_id% parameter in webapi.xml. Use the user context when the logic depends on the caller in ways the route configuration cannot express.
Safe defaults
- Create one integration per external system, each with the smallest ACL it needs.
- Prefer OAuth 1.0a signing. Enable bearer integration tokens only when the client cannot sign, and document that decision.
- Never give a third party a real admin username and password.
- Keep 2FA enabled for humans. Do not disable it to make an API script work.
- Keep customer token lifetimes short and use Force Sign-In when an account is at risk.
- Rotate integration tokens when someone with access leaves, or when you suspect a leak.
For a wider view of connecting Magento to back office systems, see the ERP integration patterns article.
FAQ
Why did my integration start returning 401 after upgrading to 2.4.4 or later?
Since 2.4.4, integration access tokens cannot be used as standalone bearer tokens unless you enable that option in the OAuth consumer settings. Either enable it or switch the client to OAuth 1.0a signed requests.
Why does the admin token endpoint fail when 2FA is enabled?
With Magento_TwoFactorAuth active, admin tokens have to be requested through a provider endpoint that includes the one time code, such as /V1/tfa/provider/google/authenticate. For unattended systems, use an integration instead.
How long do admin and customer tokens last?
By default 4 hours for admin tokens and 1 hour for customer tokens. Both are configurable under Stores, Configuration, Services, OAuth, Access Token Expiration.
Do integration tokens expire?
No. They stay valid until the integration is reauthorized or deleted, which is why they must be stored as secrets.
I am available for freelance Magento and Adobe Commerce work. If you need help connecting an external system to Magento securely, get in touch.
Related articles
Magento 2 Admin Security Hardening Checklist
Harden the Magento 2 admin: custom admin path, IP restriction, 2FA, lockout and session settings, user and role cleanup, API integrations and admin logs.
Magento 2 Admin Two Factor Authentication: Setup and Fixes
Set up Magento 2 admin two factor authentication: providers, forcing a provider, CLI commands to reset users, local development, API tokens and common problems.
Magento 2 Code Review Checklist for Pull Requests
A practical Magento 2 code review checklist: DI and architecture, performance traps, security and escaping, database changes, frontend, tests and deploy impact.
How to Create a Custom REST API Endpoint in Magento 2
Create a custom REST API endpoint in Magento 2.4.8 with webapi.xml, a service interface and ACL. Covers admin, customer and anonymous access with examples.