From c94ef134e483fb1e8635c9f8823ce2d8ba335b40 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 24 Jun 2013 15:55:41 -0700 Subject: [PATCH] Add `bin/auth refresh` for debugging OAuth token refresh issues Summary: Ref T2852. Provide a script for inspecting/debugging OAuth token refresh. Test Plan: Ran `bin/auth refresh` with various arguments, saw token refreshes. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2852 Differential Revision: https://secure.phabricator.com/D6276 --- scripts/setup/manage_auth.php | 1 + src/__phutil_library_map__.php | 2 + ...abricatorAuthManagementRefreshWorkflow.php | 144 ++++++++++++++++++ .../provider/PhabricatorAuthProviderOAuth.php | 15 +- .../bridge/DoorkeeperBridgeAsana.php | 2 +- 5 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 src/applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php diff --git a/scripts/setup/manage_auth.php b/scripts/setup/manage_auth.php index 5eddff97ca..f4c87e95d8 100755 --- a/scripts/setup/manage_auth.php +++ b/scripts/setup/manage_auth.php @@ -16,6 +16,7 @@ $args->parseStandardArguments(); $workflows = array( new PhabricatorAuthManagementRecoverWorkflow(), + new PhabricatorAuthManagementRefreshWorkflow(), new PhabricatorAuthManagementLDAPWorkflow(), new PhutilHelpArgumentWorkflow(), ); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b6ffc5b844..648aa1434c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -836,6 +836,7 @@ phutil_register_library_map(array( 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', 'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php', 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', + 'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php', 'PhabricatorAuthManagementWorkflow' => 'applications/auth/management/PhabricatorAuthManagementWorkflow.php', 'PhabricatorAuthNewController' => 'applications/auth/controller/config/PhabricatorAuthNewController.php', 'PhabricatorAuthOldOAuthRedirectController' => 'applications/auth/controller/PhabricatorAuthOldOAuthRedirectController.php', @@ -2712,6 +2713,7 @@ phutil_register_library_map(array( 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', 'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', + 'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementWorkflow' => 'PhutilArgumentWorkflow', 'PhabricatorAuthNewController' => 'PhabricatorAuthProviderConfigController', 'PhabricatorAuthOldOAuthRedirectController' => 'PhabricatorAuthController', diff --git a/src/applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php new file mode 100644 index 0000000000..e9b65abac4 --- /dev/null +++ b/src/applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php @@ -0,0 +1,144 @@ +setName('refresh') + ->setExamples('**refresh**') + ->setSynopsis( + pht( + 'Refresh OAuth access tokens. This is primarily useful for '. + 'development and debugging.')) + ->setArguments( + array( + array( + 'name' => 'user', + 'param' => 'user', + 'help' => 'Refresh tokens for a given user.', + ), + array( + 'name' => 'type', + 'param' => 'provider', + 'help' => 'Refresh tokens for a given provider type.', + ), + array( + 'name' => 'domain', + 'param' => 'domain', + 'help' => 'Refresh tokens for a given domain.', + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $console = PhutilConsole::getConsole(); + $viewer = PhabricatorUser::getOmnipotentUser(); + + $query = id(new PhabricatorExternalAccountQuery()) + ->setViewer($viewer); + + $username = $args->getArg('user'); + if (strlen($username)) { + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withUsernames(array($username)) + ->executeOne(); + if ($user) { + $query->withUserPHIDs(array($user->getPHID())); + } else { + throw new PhutilArgumentUsageException( + pht('No such user "%s"!', $username)); + } + } + + + $type = $args->getArg('type'); + if (strlen($type)) { + $query->withAccountTypes(array($type)); + } + + $domain = $args->getArg('domain'); + if (strlen($domain)) { + $query->withAccountDomains(array($domain)); + } + + $accounts = $query->execute(); + + if (!$accounts) { + throw new PhutilArgumentUsageException( + pht("No accounts match the arguments!")); + } else { + $console->writeOut( + "%s\n", + pht( + "Found %s accounts to refresh.", + new PhutilNumber(count($accounts)))); + } + + $providers = PhabricatorAuthProvider::getAllEnabledProviders(); + + foreach ($accounts as $account) { + $console->writeOut( + "%s\n", + pht( + "Refreshing account #%d (%s/%s).", + $account->getID(), + $account->getAccountType(), + $account->getAccountDomain())); + + $key = $account->getProviderKey(); + if (empty($providers[$key])) { + $console->writeOut( + "> %s\n", + pht("Skipping, provider is not enabled or does not exist.")); + continue; + } + + $provider = $providers[$key]; + if (!($provider instanceof PhabricatorAuthProviderOAuth)) { + $console->writeOut( + "> %s\n", + pht("Skipping, provider is not an OAuth provider.")); + continue; + } + + $adapter = $provider->getAdapter(); + if (!$adapter->supportsTokenRefresh()) { + $console->writeOut( + "> %s\n", + pht("Skipping, provider does not support token refresh.")); + continue; + } + + $refresh_token = $account->getProperty('oauth.token.refresh'); + if (!$refresh_token) { + $console->writeOut( + "> %s\n", + pht("Skipping, provider has no stored refresh token.")); + continue; + } + + $console->writeOut( + "+ %s\n", + pht( + "Refreshing token, current token expires in %s seconds.", + new PhutilNumber( + $account->getProperty('oauth.token.access.expires') - time()))); + + $adapter->refreshAccessToken($refresh_token); + + $console->writeOut( + "+ %s\n", + pht( + "Refreshed token, new token expires in %s seconds.", + new PhutilNumber($adapter->getAccessTokenExpires() - time()))); + + } + + $console->writeOut("%s\n", pht("Done.")); + + return 0; + } + +} diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php index a945a8760c..e83aa81e69 100644 --- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php +++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php @@ -284,9 +284,20 @@ abstract class PhabricatorAuthProviderOAuth extends PhabricatorAuthProvider { protected function willSaveAccount(PhabricatorExternalAccount $account) { parent::willSaveAccount($account); - $oauth_token = $this->getAdapter()->getAccessToken(); - $account->setProperty('oauth.token', $oauth_token); + $adapter = $this->getAdapter(); + $oauth_token = $adapter->getAccessToken(); + $account->setProperty('oauth.token.access', $oauth_token); + + if ($adapter->supportsTokenRefresh()) { + $refresh_token = $adapter->getRefreshToken(); + $account->setProperty('oauth.token.refresh', $refresh_token); + } else { + $account->setProperty('oauth.token.refresh', null); + } + + $expires = $adapter->getAccessTokenExpires(); + $account->setProperty('oauth.token.access.expires', $expires); } } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php index e8be40cbd6..d40e08dad0 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php @@ -35,7 +35,7 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge { // right now so this is currently moot. $account = head($accounts); - $token = $account->getProperty('oauth.token'); + $token = $account->getProperty('oauth.token.access'); if (!$token) { return; }