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;
    }