Skip to content

3.3.0 - OAuth state verification

Choose a tag to compare

@tomsommer tomsommer released this 07 Sep 18:03
· 2 commits to master since this release

The OAuth callback can now verify state.

authorize() has always returned a URL carrying a random state, but nothing could check it on the way back: finishAuthorize() took only the code, the facade exposed no way to reach the state, and the README's callback example spent the code without looking at it.

Upstream did have a check — it compared the returned state against crypt() of a fixed provider id, which is deterministic and therefore not a check. Removing that vestige in 2.0.0 left the gap visible rather than disguised.

Without a state check, a callback cannot be told apart from one an attacker made the visitor follow, which is login CSRF / authorization-code injection.

What changed

$authUrl = $api->authorize();
$_SESSION['onpay_oauth_state'] = $api->getState();   // new
header('Location: ' . $authUrl);

// on the callback
$api->finishAuthorize(
    $_GET['code'],
    $_GET['state'] ?? null,
    $_SESSION['onpay_oauth_state'] ?? null
);

The two states are compared with hash_equals() before the code is spent, so a mismatch never reaches the token endpoint.

This is additive: finishAuthorize($code) with no state behaves as it did, for callers who already do the comparison themselves. Supplying one half of the pair and not the other is treated as a mistake rather than a pass.

Both README flows — the plain one and the PKCE one — now store the state before redirecting and hand it back.

Integrations authenticating with StaticToken never enter this flow and are unaffected.