/** * Generates a new token against an authorization_code * Auth code is checked against clientId, clientSecret and redirectUri as registered for client in admin * Auth code is for one-use only and will be removed once the access token generated * @param string $clientId Client identifier * @param string $clientSecret Client secret key * @param string $authCode Authorization code provided by the client * @param string $redirectUri Redirect URI. Must be the same as registered in admin * @return ezpRestToken * @throws ezpOauthInvalidRequestException * @throws ezpOauthInvalidTokenException * @throws ezpOauthExpiredTokenException */ public static function doRefreshTokenWithAuthorizationCode($clientId, $clientSecret, $authCode, $redirectUri) { $client = ezpRestClient::fetchByClientId($clientId); $tokenTTL = (int) eZINI::instance('rest.ini')->variable('OAuthSettings', 'TokenTTL'); if (!$client instanceof ezpRestClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->validateSecret($clientSecret)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->isEndPointValid($redirectUri)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_REQUEST); } $session = ezcPersistentSessionInstance::get(); $q = $session->createFindQuery('ezpRestAuthcode'); $q->where($q->expr->eq('id', $q->bindValue($authCode))); $codeInfo = $session->find($q, 'ezpRestAuthcode'); if (empty($codeInfo)) { throw new ezpOauthInvalidTokenException("Specified authorization code does not exist."); } $codeInfo = array_shift($codeInfo); // Validate client is still authorized, then validate code is not expired $authorized = ezpRestAuthorizedClient::fetchForClientUser($client, eZUser::fetch($codeInfo->user_id)); if (!$authorized instanceof ezpRestAuthorizedClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } // Check expiry of authorization_code if ($codeInfo->expirytime != 0) { if ($codeInfo->expirytime < time()) { $d = date("c", $codeInfo->expirytime); throw new ezpOauthExpiredTokenException("Authorization code expired on {$d}"); } } // code ok, create access and refresh tokens $accessToken = ezpRestToken::generateToken(''); $refreshToken = ezpRestToken::generateToken(''); $token = new ezpRestToken(); $token->id = $accessToken; $token->refresh_token = $refreshToken; $token->client_id = $clientId; $token->user_id = $codeInfo->user_id; $token->expirytime = time() + $tokenTTL; $session = ezcPersistentSessionInstance::get(); $session->save($token); // After an auth code is used, we'll remove it so it is not abused $session->delete($codeInfo); return $token; }
if (($pClientId = getHTTPVariable('client_id')) === false) { error($pRedirectUri, 'invalid_request', "Missing client ID parameter"); // redirect to the redirect_uri with GET parameter error=invalid_request // @todo mismatch between chapters 3.2's example (error=access-denied) and error codes list at 3.2.1 (error=access_denied) } // Requested response type if (($pResponseType = getHTTPVariable('response_type')) === false) { error($pRedirectUri, 'invalid_request', "Missing response_type parameter"); // redirect to the redirect_uri with GET parameter error=invalid_request // @todo mismatch between chapters 3.2's example (error=access-denied) and error codes list at 3.2.1 (error=access_denied) } // Scope $pScope = getHTTPVariable('scope'); // @todo The spec mentions (FIXME:chapter) throwing an error if the request has extra parameters. Check this. // Try loading the REST client based on the ID $application = ezpRestClient::fetchByClientId($pClientId); if (!$application instanceof ezpRestClient) { error($pRedirectUri, 'invalid_client'); } // The client is found, validate the redirect_uri if (!$application->isEndPointValid($pRedirectUri)) { error($pRedirectUri, 'redirect_uri_mismatch'); } // authentication $user = eZUser::currentUser(); // login is like REALLY required here. But we can't use the standard policy check, as it won't redirect w/ GET parameters if (!$user->isRegistered()) { $redirectUri = str_replace(eZSys::indexDir(), '', $_SERVER['REQUEST_URI']); $tpl = eZTemplate::factory(); $tpl->setVariable('redirect_uri', $redirectUri, 'User'); $tpl->setVariable('site_access', array('allowed' => true));
/** * Handles the POST request which is sent to obtain token data. * * Currently only authorization code access grant is supported, section 4.1.1 * * @return ezcMvcResult */ public function doHandleRequest() { // Check that the correct params are present // Params for token endpoint per section 4 // REQUIRED: grant_type, client_id, client_secret // OPTIONAL: scope // // Supported grant types are: authorization_code and refresh_token // // Additional params for 'authorization_code', Section 4.1.1 // REQUIRED: code, redirect_uri // // Additional param for 'refresh_token", Section 4.1.4 // REQUIRED: refresh_token // Defining the required params for each stage of operation $initialRequiredParams = array('grant_type', 'client_id', 'client_secret'); // params for grant_type=authorization_code $codeRequiredParams = array('code', 'redirect_uri'); // params for grant_type=refresh_token $refreshRequiredParams = array('refresh_token'); $this->checkParams($initialRequiredParams); // We can get the first set of required params $grant_type = $this->request->post['grant_type']; $client_id = $this->request->post['client_id']; $client_secret = $this->request->post['client_secret']; if (!$this->validateGrantType($grant_type)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::UNSUPPORTED_GRANT_TYPE); } switch ($grant_type) { case 'authorization_code': $this->checkParams($codeRequiredParams); $authCode = $this->request->post['code']; $redirect_uri = $this->request->post['redirect_uri']; $client = ezpRestClient::fetchByClientId($client_id); if (!$client instanceof ezpRestClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->validateSecret($client_secret)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->isEndPointValid($redirect_uri)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_REQUEST); } $session = ezcPersistentSessionInstance::get(); $q = $session->createFindQuery('ezpRestAuthcode'); $q->where($q->expr->eq('id', $q->bindValue($authCode))); $codeInfo = $session->find($q, 'ezpRestAuthcode'); if (empty($codeInfo)) { // return self::STATUS_TOKEN_INVALID; throw new ezpOauthInvalidTokenException("Specified authorization code does not exist."); } $codeInfo = array_shift($codeInfo); // Validate client is still authorized, then validate code is not expired $authorized = ezpRestAuthorizedClient::fetchForClientUser($client, eZUser::fetch($codeInfo->user_id)); if (!$authorized instanceof ezpRestAuthorizedClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } // Check expiry of token if ($codeInfo->expirytime !== 0) { if ($codeInfo->expirytime < time()) { $d = date("c", $codeInfo->expirytime); // return self::STATUS_TOKEN_EXPIRED; throw new ezpOauthExpiredTokenException("Token expired on {$d}"); } } // code eok, create access token $accessToken = ezpRestToken::generateToken(''); $refreshToken = ezpRestToken::generateToken(''); $token = new ezpRestToken(); $token->id = $accessToken; $token->refresh_token = $refreshToken; $token->client_id = $client_id; $token->user_id = $codeInfo->user_id; $token->expirytime = time() + 3600; $session = ezcPersistentSessionInstance::get(); $session->save($token); //@TODO for later, check for transmission over https $r = new ezcMvcResult(); $r->status = new ezcMvcExternalRedirect($redirect_uri . '?access_token=' . $token->id); return $r; break; case 'refresh_token': $this->checkParams($refreshRequiredParams); $refreshToken = $this->request->post['refresh_token']; break; } }