@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Manage OAuth1 request token secrets in core OAuth1 workflow

Summary:
Ref T5096. Ref T4251. See D9202 for discussion.

- Twitter seems to accept either one (?!?!?!??).
- JIRA uses RSA-SHA1, which does not depend on the token secret.
- This change makes Bitbucket work.

Test Plan:
- OAuthed with Twitter.
- OAuthed with JIRA.
- OAuthed with some Bitbucket code I had partially laying around in a partial state, which works after this change.

Reviewers: csteipp, btrahan, 20after4

Reviewed By: 20after4

Subscribers: epriestley

Maniphest Tasks: T4251, T5096

Differential Revision: https://secure.phabricator.com/D9760

+78
+75
src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php
··· 9 9 const PROPERTY_CONSUMER_SECRET = 'oauth1:consumer:secret'; 10 10 const PROPERTY_PRIVATE_KEY = 'oauth1:private:key'; 11 11 12 + const TEMPORARY_TOKEN_TYPE = 'oauth1:request:secret'; 13 + 12 14 protected function getIDKey() { 13 15 return self::PROPERTY_CONSUMER_KEY; 14 16 } ··· 55 57 $adapter->setCallbackURI($callback_uri); 56 58 57 59 $uri = $adapter->getClientRedirectURI(); 60 + 61 + $this->saveHandshakeTokenSecret( 62 + $client_code, 63 + $adapter->getTokenSecret()); 64 + 58 65 $response = id(new AphrontRedirectResponse())->setURI($uri); 59 66 return array($account, $response); 60 67 } ··· 84 91 85 92 $adapter->setToken($token); 86 93 $adapter->setVerifier($verifier); 94 + 95 + $client_code = $this->getAuthCSRFCode($request); 96 + $token_secret = $this->loadHandshakeTokenSecret($client_code); 97 + $adapter->setTokenSecret($token_secret); 87 98 88 99 // NOTE: As a side effect, this will cause the OAuth adapter to request 89 100 // an access token. ··· 195 206 $item->addAttribute(pht('OAuth1 Account')); 196 207 197 208 parent::willRenderLinkedAccount($viewer, $item, $account); 209 + } 210 + 211 + 212 + /* -( Temporary Secrets )-------------------------------------------------- */ 213 + 214 + 215 + private function saveHandshakeTokenSecret($client_code, $secret) { 216 + $key = $this->getHandshakeTokenKeyFromClientCode($client_code); 217 + $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE); 218 + 219 + // Wipe out an existing token, if one exists. 220 + $token = id(new PhabricatorAuthTemporaryTokenQuery()) 221 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 222 + ->withObjectPHIDs(array($key)) 223 + ->withTokenTypes(array($type)) 224 + ->executeOne(); 225 + if ($token) { 226 + $token->delete(); 227 + } 228 + 229 + // Save the new secret. 230 + id(new PhabricatorAuthTemporaryToken()) 231 + ->setObjectPHID($key) 232 + ->setTokenType($type) 233 + ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 234 + ->setTokenCode($secret) 235 + ->save(); 236 + } 237 + 238 + private function loadHandshakeTokenSecret($client_code) { 239 + $key = $this->getHandshakeTokenKeyFromClientCode($client_code); 240 + $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE); 241 + 242 + $token = id(new PhabricatorAuthTemporaryTokenQuery()) 243 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 244 + ->withObjectPHIDs(array($key)) 245 + ->withTokenTypes(array($type)) 246 + ->withExpired(false) 247 + ->executeOne(); 248 + 249 + if (!$token) { 250 + throw new Exception( 251 + pht( 252 + 'Unable to load your OAuth1 token secret from storage. It may '. 253 + 'have expired. Try authenticating again.')); 254 + } 255 + 256 + return $token->getTokenCode(); 257 + } 258 + 259 + private function getTemporaryTokenType($core_type) { 260 + // Namespace the type so that multiple providers don't step on each 261 + // others' toes if a user starts Mediawiki and Bitbucket auth at the 262 + // same time. 263 + 264 + return $core_type.':'.$this->getProviderConfig()->getID(); 265 + } 266 + 267 + private function getHandshakeTokenKeyFromClientCode($client_code) { 268 + // NOTE: This is very slightly coersive since the TemporaryToken table 269 + // expects an "objectPHID" as an identifier, but nothing about the storage 270 + // is bound to PHIDs. 271 + 272 + return 'oauth1:secret/'.$client_code; 198 273 } 199 274 200 275 }
+3
src/applications/auth/storage/PhabricatorAuthTemporaryToken.php
··· 3 3 final class PhabricatorAuthTemporaryToken extends PhabricatorAuthDAO 4 4 implements PhabricatorPolicyInterface { 5 5 6 + // TODO: OAuth1 stores a client identifier here, which is not a real PHID. 7 + // At some point, we should rename this column to be a little more generic. 6 8 protected $objectPHID; 9 + 7 10 protected $tokenType; 8 11 protected $tokenExpires; 9 12 protected $tokenCode;