public static function doRefreshToken( $clientId, $clientSecret, $refreshToken )
    {
        $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 );

        $session = ezcPersistentSessionInstance::get();

        $q = $session->createFindQuery( 'ezpRestToken' );
        $q->where( $q->expr->eq( 'refresh_token', $q->bindValue( $refreshToken ) ) );
        $refreshInfo = $session->find( $q, 'ezpRestToken' );

        if ( empty( $refreshInfo) )
        {
            throw new ezpOauthInvalidRequestException( "Specified refresh-token does not exist." );
        }

        $refreshInfo = array_shift( $refreshInfo );

        // Validate client is still authorized, then validate code is not expired
        $authorized = ezpRestAuthorizedClient::fetchForClientUser( $client, eZUser::fetch( $refreshInfo->user_id ) );

        if ( !($authorized instanceof ezpRestAuthorizedClient ) )
        {
            throw new ezpOauthInvalidRequestException( ezpOauthTokenEndpointErrorType::INVALID_CLIENT );
        }


        // Ideally there should be a separate expiry for refresh tokens here, for now allow.
        $newToken = new ezpRestToken();
        $newToken->id = ezpRestToken::generateToken( '' );
        $newToken->refresh_token = ezpRestToken::generateToken( '' );
        $newToken->client_id = $clientId;
        $newToken->user_id = $refreshInfo->user_id;
        $newToken->expirytime = time() + $tokenTTL;

        $session->save( $newToken );
        $session->delete( $refreshInfo );

        return $newToken;
    }