@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
at recaptime-dev/main 195 lines 6.4 kB view raw
1<?php 2 3final class PhabricatorOAuthServerTokenController 4 extends PhabricatorOAuthServerController { 5 6 public function shouldRequireLogin() { 7 return false; 8 } 9 10 public function shouldAllowRestrictedParameter($parameter_name) { 11 if ($parameter_name == 'code') { 12 return true; 13 } 14 return parent::shouldAllowRestrictedParameter($parameter_name); 15 } 16 17 public function handleRequest(AphrontRequest $request) { 18 $grant_type = $request->getStr('grant_type'); 19 $code = $request->getStr('code'); 20 $redirect_uri = $request->getStr('redirect_uri'); 21 $response = new PhabricatorOAuthResponse(); 22 $server = new PhabricatorOAuthServer(); 23 24 $client_id_parameter = $request->getStr('client_id'); 25 $client_id_header = idx($_SERVER, 'PHP_AUTH_USER'); 26 if (phutil_nonempty_string($client_id_parameter) && 27 phutil_nonempty_string($client_id_header)) { 28 if ($client_id_parameter !== $client_id_header) { 29 throw new Exception( 30 pht( 31 'Request included a client_id parameter and an "Authorization" '. 32 'header with a username, but the values "%s" and "%s") disagree. '. 33 'The values must match.', 34 $client_id_parameter, 35 $client_id_header)); 36 } 37 } 38 39 $client_secret_parameter = $request->getStr('client_secret'); 40 $client_secret_header = idx($_SERVER, 'PHP_AUTH_PW'); 41 if (strlen($client_secret_parameter)) { 42 // If the `client_secret` parameter is present, prefer parameters. 43 $client_phid = $client_id_parameter; 44 $client_secret = $client_secret_parameter; 45 } else { 46 // Otherwise, read values from the "Authorization" header. 47 $client_phid = $client_id_header; 48 $client_secret = $client_secret_header; 49 } 50 51 if ($grant_type != 'authorization_code') { 52 $response->setError('unsupported_grant_type'); 53 $response->setErrorDescription( 54 pht( 55 'Only %s %s is supported.', 56 'grant_type', 57 'authorization_code')); 58 return $response; 59 } 60 61 if (!$code) { 62 $response->setError('invalid_request'); 63 $response->setErrorDescription(pht('Required parameter code missing.')); 64 return $response; 65 } 66 67 if (!$client_phid) { 68 $response->setError('invalid_request'); 69 $response->setErrorDescription( 70 pht( 71 'Required parameter %s missing.', 72 'client_id')); 73 return $response; 74 } 75 76 if (!$client_secret) { 77 $response->setError('invalid_request'); 78 $response->setErrorDescription( 79 pht( 80 'Required parameter %s missing.', 81 'client_secret')); 82 return $response; 83 } 84 85 // one giant try / catch around all the exciting database stuff so we 86 // can return a 'server_error' response if something goes wrong! 87 try { 88 $auth_code = id(new PhabricatorOAuthServerAuthorizationCode()) 89 ->loadOneWhere('code = %s', 90 $code); 91 if (!$auth_code) { 92 $response->setError('invalid_grant'); 93 $response->setErrorDescription( 94 pht( 95 'Authorization code %s not found.', 96 $code)); 97 return $response; 98 } 99 100 // if we have an auth code redirect URI, there must be a redirect_uri 101 // in the request and it must match the auth code redirect uri *exactly* 102 $auth_code_redirect_uri = $auth_code->getRedirectURI(); 103 if ($auth_code_redirect_uri) { 104 $auth_code_redirect_uri = new PhutilURI($auth_code_redirect_uri); 105 $redirect_uri = new PhutilURI($redirect_uri); 106 if (!$redirect_uri->getDomain() || 107 $redirect_uri != $auth_code_redirect_uri) { 108 $response->setError('invalid_grant'); 109 $response->setErrorDescription( 110 pht( 111 'Redirect URI in request must exactly match redirect URI '. 112 'from authorization code.')); 113 return $response; 114 } 115 } else if ($redirect_uri) { 116 $response->setError('invalid_grant'); 117 $response->setErrorDescription( 118 pht( 119 'Redirect URI in request and no redirect URI in authorization '. 120 'code. The two must exactly match.')); 121 return $response; 122 } 123 124 $client = id(new PhabricatorOAuthServerClient()) 125 ->loadOneWhere('phid = %s', $client_phid); 126 if (!$client) { 127 $response->setError('invalid_client'); 128 $response->setErrorDescription( 129 pht( 130 'Client with %s %s not found.', 131 'client_id', 132 $client_phid)); 133 return $response; 134 } 135 136 if ($client->getIsDisabled()) { 137 $response->setError('invalid_client'); 138 $response->setErrorDescription( 139 pht( 140 'OAuth application "%s" has been disabled.', 141 $client->getName())); 142 143 return $response; 144 } 145 146 $server->setClient($client); 147 148 $user_phid = $auth_code->getUserPHID(); 149 $user = id(new PhabricatorUser()) 150 ->loadOneWhere('phid = %s', $user_phid); 151 if (!$user) { 152 $response->setError('invalid_grant'); 153 $response->setErrorDescription( 154 pht( 155 'User with PHID %s not found.', 156 $user_phid)); 157 return $response; 158 } 159 $server->setUser($user); 160 161 $test_code = new PhabricatorOAuthServerAuthorizationCode(); 162 $test_code->setClientSecret($client_secret); 163 $test_code->setClientPHID($client_phid); 164 $is_good_code = $server->validateAuthorizationCode( 165 $auth_code, 166 $test_code); 167 if (!$is_good_code) { 168 $response->setError('invalid_grant'); 169 $response->setErrorDescription( 170 pht( 171 'Invalid authorization code %s.', 172 $code)); 173 return $response; 174 } 175 176 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 177 $access_token = $server->generateAccessToken(); 178 $auth_code->delete(); 179 unset($unguarded); 180 $result = array( 181 'access_token' => $access_token->getToken(), 182 'token_type' => 'Bearer', 183 ); 184 return $response->setContent($result); 185 } catch (Exception $e) { 186 $response->setError('server_error'); 187 $response->setErrorDescription( 188 pht( 189 'The authorization server encountered an unexpected condition '. 190 'which prevented it from fulfilling the request.')); 191 return $response; 192 } 193 } 194 195}