Skip to content

Return cached admin token before decrypting credentials#927

Open
lbajsarowicz wants to merge 1 commit into
magento:developfrom
lbajsarowicz:perf/webapiauth-cache-before-decrypt
Open

Return cached admin token before decrypting credentials#927
lbajsarowicz wants to merge 1 commit into
magento:developfrom
lbajsarowicz:perf/webapiauth-cache-before-decrypt

Conversation

@lbajsarowicz

Copy link
Copy Markdown
Contributor

Description

WebApiAuth::getAdminToken() caches the admin bearer token per login in
self::$adminAuthTokens (with a ~4h freshness check in hasExistingToken()). However,
the method decrypts the admin password via CredentialStore before it checks that
cache:

$login = $username ?? getenv('MAGENTO_ADMIN_USERNAME');
try {
    $encryptedSecret = CredentialStore::getInstance()->getSecret('magento/MAGENTO_ADMIN_PASSWORD');
    $secret = CredentialStore::getInstance()->decryptSecretValue($encryptedSecret); // expensive
    $password = $password ?? $secret;
} catch (TestFrameworkException $e) { ... }
// validation ...
if (self::hasExistingToken($login)) {          // cache check happens AFTER decrypt
    return self::$adminAuthTokens[$login];
}

A new executor authorizes on construction for every createData / deleteData /
magentoCLI operation, so this runs a credential decrypt on every fixture call even when
the token is already cached and no decrypt is needed.

Fixed Issues

Part of #925

Fix

$login is fully determined ($username ?? getenv(...)) before any decryption, and the
cache key depends only on $login. Move the cache check ahead of the decrypt so a cache
hit short-circuits before touching CredentialStore:

$login = $username ?? getenv('MAGENTO_ADMIN_USERNAME');

if (self::hasExistingToken($login)) {
    return self::$adminAuthTokens[$login];
}

try {
    $encryptedSecret = CredentialStore::getInstance()->getSecret('magento/MAGENTO_ADMIN_PASSWORD');
    ...

The cache-miss path (decrypt, empty-credential validation, error messages, HTTP auth,
token storage) is byte-for-byte unchanged; only the position of the cache-check block
moves.

Security impact

  • Handles credentials: yes — admin password decryption via CredentialStore.
  • Change: the fix decrypts the admin password less often (only on a cache miss),
    never more. What is decrypted, how it is stored, and the auth request payload are all
    unchanged. No secret values are logged or added to any error context by this change.
  • Net effect: neutral-to-positive — fewer decrypt operations, no change to the trust
    boundary or to what is transmitted.

Manual testing scenarios

  1. Run a test with several createData/deleteData steps sharing one admin login and
    confirm fixtures still create/delete correctly (token reuse path).
  2. Confirm a cold run (empty token cache) still authenticates and, with missing
    credentials, still throws the existing FastFailException.

Unit test

Adds WebApiAuthTest asserting the cache-hit path returns the cached token without
invoking CredentialStore decryption (mock configured with expects($this->never())).

Contribution checklist

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • Unit test added; unit suite passes locally against develop

WebApiAuth::getAdminToken() decrypted the admin password via CredentialStore
before checking the per-login token cache. Because a new executor authorizes
on construction for every createData/deleteData/magentoCLI operation, this ran
a needless decrypt on every fixture call even when the token was already
cached. The cache key depends only on $login, which is known before
decryption, so the cache check can safely run first.

Adds a unit test asserting the cache hit path never invokes CredentialStore.

Ref magento#925
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant