Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions tests/Unit/Auth0Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,56 @@ public function defer(
expect($credentials)->toBeNull();
});

test('getCredentials() enforces a queued backchannel logout on a subsequent request', function(): void {
$issuer = 'https://' . $this->configuration['domain'] . '/';
$sid = uniqid();

$token = (new TokenGenerator())->withHs256([
'sid' => $sid,
'iss' => $issuer,
]);

$pool = new ArrayAdapter();

// Shared session storage so a subsequent instance sees the persisted session.
$config = array_merge($this->configuration, [
'tokenAlgorithm' => 'HS256',
'backchannelLogoutCache' => $pool,
'sessionStorage' => new SessionStore(new SdkConfiguration($this->configuration), 'auth0_session'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three things about this setup line.

The ticket says all storage backends are affected, but this only exercises SessionStore. The default is CookieStore, so the configuration most people actually run is the one with no coverage. The bug was in getState(), which is backend agnostic, so a CookieStore case should pass without any extra production changes and would cover the default path.

new SdkConfiguration($this->configuration) builds a second configuration object just to hand to the store, separate from the one the Auth0 instance ends up creating from $config. It works, since SessionStore only uses the config for cookie params during start(), but it reads like it might be intentional when I think it is just awkward. Worth a comment or a restructure.

Last one: 'auth0_session' is a fixed namespace and nothing resets $_SESSION. beforeEach only clears $_GET and $_COOKIE, and there are other tests in this file using SessionStore at 374 and 821. pest:ci runs with --order-by=random and --fail-on-risky, so this is the kind of leak that turns into a confusing failure on some unrelated PR later. A uniqid() namespace, or $_SESSION = []; in beforeEach, would sort it.

]);

$auth0 = new Auth0($config);
$auth0->authentication()->getHttpClient()->mockResponses([
HttpResponseGenerator::create('{"access_token":"1.2.3","id_token":"' . $token . '","refresh_token":"4.5.6","scope":"test:part1,test:part2","expires_in":300}'),
HttpResponseGenerator::create('{"sub":"__test_sub__"}'),
]);

$_GET['code'] = uniqid();
$_GET['state'] = '__test_state__';
$auth0->configuration()->getTransientStorage()->set('state', '__test_state__');
$auth0->configuration()->getTransientStorage()->set('nonce', '__test_nonce__');
$auth0->configuration()->getTransientStorage()->set('code_verifier', '__test_code_verifier__');

expect($auth0->exchange())->toBeTrue();

$logoutToken = TokenGenerator::create(
tokenType: TokenGenerator::TOKEN_LOGOUT,
algorithm: TokenGenerator::ALG_HS256,
claims: [
'sub' => '__test_sub__',
'iss' => $issuer,
'sid' => $sid,
],
);

$auth0->handleBackchannelLogout($logoutToken->token);

// A subsequent request: a fresh instance must rehydrate the backchannel key
// from session storage for the queued logout to be enforced.
$fresh = new Auth0($config);
expect($fresh->getCredentials())->toBeNull();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is right and it does fail without the fix, which I checked.

Two suggestions to make it harder to pass by accident. Right now toBeNull() is also what you would get if the session simply failed to rehydrate, for example a storage mix up or a broken exchange, so a future regression that breaks rehydration entirely would still show green here. Asserting that credentials exist first, then queueing the logout, then asserting null, is what the older test at 1140 does and it pins the cause down:

$fresh = new Auth0($config);
expect($fresh->getCredentials())->not->toBeNull();  // rehydration works
// then queue the logout and assert null on another fresh instance

Second, could we add a 'persistIdToken' => false variant? That is the configuration I flagged on src/Auth0.php, where the key is rehydrated but the enforcement block is skipped anyway. It fails today, so it would only go in alongside the guard change, but it is the case most likely to quietly regress.

});

test('setIdToken() properly stores data', function(): void {
$token = (new TokenGenerator())->withHs256();
$auth0 = new Auth0($this->configuration + [
Expand Down
Loading