{"record":{"id":"b9d628d97151a0c5","repo":"phacility/phabricator","slug":"expected-verifier-to-finish-oauth-handshake","errorCode":null,"errorMessage":"Expected verifier to finish OAuth handshake!","messagePattern":"Expected verifier to finish OAuth handshake!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php","lineNumber":169,"sourceCode":"        pht(\"Expected '%s' to be '%s'!\", 'oauth_callback_confirmed', 'true'));\n    }\n\n    $this->readTokenAndTokenSecret($data);\n\n    $authorize_token_uri = new PhutilURI($this->getAuthorizeTokenURI());\n    $authorize_token_uri->replaceQueryParam('oauth_token', $this->getToken());\n\n    return phutil_string_cast($authorize_token_uri);\n  }\n\n  protected function finishOAuthHandshake() {\n    $this->willFinishOAuthHandshake();\n\n    if (!$this->getToken()) {\n      throw new Exception(pht('Expected token to finish OAuth handshake!'));\n    }\n    if (!$this->getVerifier()) {\n      throw new Exception(pht('Expected verifier to finish OAuth handshake!'));\n    }\n\n    $validate_uri = $this->getValidateTokenURI();\n    $params = array(\n      'oauth_verifier' => $this->getVerifier(),\n    );\n\n    list($body) = $this->newOAuth1Future($validate_uri, $params)->resolvex();\n    $data = id(new PhutilQueryStringParser())->parseQueryString($body);\n\n    $this->readTokenAndTokenSecret($data);\n\n    $this->handshakeData = $data;\n  }\n\n  private function readTokenAndTokenSecret(array $data) {\n    $token = idx($data, 'oauth_token');\n    if (!$token) {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php#L151-L187","documentation":"finishOAuthHandshake() requires an oauth_verifier value (RFC 5849 section 2.2) before it will exchange the request token for an access token. getVerifier() returning empty means the provider's redirect back to your callback did not include (or did not preserve) the verifier parameter.","triggerScenarios":"The provider redirects to the callback without oauth_verifier (plain OAuth 1.0 behavior or a provider quirk); the callback URI was configured in a way that strips query parameters (e.g., a rewrite rule or a redirect chain that drops the query string); setVerifier() was never called with $request->getStr('oauth_verifier') before the handshake completion; the user hit the callback URL manually.","commonSituations":"Callback URL configured on the provider that already contains a query string so the verifier gets appended incorrectly or dropped; middleware/redirects normalizing the URL and discarding query params; provider implementing OAuth 1.0 instead of 1.0a; direct navigation/bookmark of the callback URI.","solutions":["Confirm the provider redirects back with both oauth_token and oauth_verifier in the query string (inspect the callback request URL).","Make sure your code reads oauth_verifier from the request and calls setVerifier() before finishing the handshake.","Fix any redirect/rewrite rules between the provider and your endpoint that discard query parameters.","If the provider only supports OAuth 1.0 (no verifier), it cannot be used with this 1.0a-only adapter."],"exampleFix":"// before: handshake finishes without a verifier\n$adapter->finishOAuthHandshake();\n\n// after: pass the verifier from the provider callback through\n$adapter->setVerifier($request->getStr('oauth_verifier'));\n$adapter->finishOAuthHandshake();","handlingStrategy":"validation","validationCode":"// Check the callback parameters before finishing the handshake\n$verifier = $request->getStr('oauth_verifier');\nif (!strlen($verifier)) {\n  return $this->restartAuthenticateFlow();\n}\n$adapter->setVerifier($verifier);","typeGuard":null,"tryCatchPattern":"try {\n  $adapter->finishOAuthHandshake();\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'verifier') !== false) {\n    // Provider redirect lost oauth_verifier; restart the flow.\n    return $this->restartAuthenticateFlow();\n  }\n  throw $ex;\n}","preventionTips":["Inspect the callback URL for oauth_token and oauth_verifier before invoking the finish phase.","Keep callback URLs parameter-clean (no existing query string) so the provider appends verifier params.","Avoid redirect chains between provider and app that strip query parameters."],"tags":["oauth1","authentication","handshake","callback","php"],"backgroundTag":"oauth1-handshake-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}