Войдите в учетную запись Google со старым паролем - как перенаправить на синюю страницу входа в Google?

Я внедрил Google Sign-In SDK в свое приложение, и он работает нормально. Когда я нажимаю кнопку входа, открывается окно с уже сохраненными учетными записями. Выбор одной из этих учетных записей успешно завершает процесс входа.

Единственный вариант использования, который не проходит, - это когда пользователь попадает в диалоговое окно входа и нажимает на учетную запись с неверным паролем. Я не уверен, как решить эту проблему.


Я последовал инструкции Google "реализовать SDK для входа" и после вызова этих строк:

Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount googleSignInAccount = task.getResult(ApiException.class);

Я ловлю исключение с кодом состояния 12501 SIGN_IN_CANCELLED.

Как я уже говорил, это происходит из-за того, что одна из хранимых учетных записей имеет неверный пароль. Вот шаги для воспроизведения:

  1. пользователь вошел в систему один раз
  2. диалог сохранил свои полномочия
  3. Тем временем пользователь изменил пароль своей учетной записи на www
  4. пользователь выбирает сохраненные учетные данные 5. происходит несвязанный код ошибки).

Как я могу заставить пользователя перенаправить на эту синюю страницу входа в Google и сохранить текущий поток?

Например, AliExpress каким-то образом справится с этим и перенаправит пользователя на синюю страницу с просьбой снова войти в систему.

enter image description here

Мой код мало чем отличается от инструкции Google. Это мой поток кода. Все начинается с onClick():

В методе onClick():

// Logout before all operations
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account != null) {
    mGoogleSignInClient.signOut();
}

// Call to sign in
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, RequestCodes.RC_GOOGLE_SIGN_IN);

В разделе onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");

    if (requestCode == RequestCodes.RC_GOOGLE_SIGN_IN) {

        try {

            // Call to take account data
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);

            // Fetch account data
            GoogleSignInAccount googleSignInAccount = task.getResult(ApiException.class);

            Account account = googleSignInAccount.getAccount();

            // Calling to get short lived token
            String shortLivedToken = GoogleAuthUtil.getToken(mContext, account, "oauth2:" + Scopes.PROFILE + " " + Scopes.EMAIL);

            // Further calls here...

        } catch (ApiException e) {

            //https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInStatusCodes

            if (e.getStatusCode() == 12501) {
                Log.e(TAG, "SIGN_IN_CANCELLED");
            } else if (e.getStatusCode() == 12502) {
                Log.e(TAG, "SIGN_IN_CURRENTLY_IN_PROGRESS");
            } else if (e.getStatusCode() == 12500) {
                Log.e(TAG, "SIGN_IN_FAILED");
            } else {
                e.printStackTrace();
            }

        } catch (GoogleAuthException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Ответы

Ответ 1

Отказ от ответственности Я не сотрудник Google. Все, что я скажу ниже, это мои выводы из исследования подобных проблем.

Короткий ответ

Вы все делаете правильно. Это рекомендуемый способ входа в учетную запись Google. К сожалению, в этом механизме нет фактического обратного вызова, чтобы указать, что на самом деле пошло не так в вашем случае. Службы Google Play обрабатывают это, уведомляя пользователя о том, что его учетные данные устарели из-за уведомлений, которые вы можете увидеть ниже (сразу после изменения пароля).

notification

Я бы порекомендовал сообщить об ошибке при добавлении дополнительных кодов результатов для вашего дела на https://issuetracker.google.com, так как это кажется заметным улучшением.

Длинный ответ

Google использует API учетной записи Android, как и все остальные (вы можете попробовать это сами). За кулисами это просто механизм поиска и хранения oauth-токенов.

Когда пароль был изменен, токен больше не действителен, и вы пытаетесь использовать его.

Это работает так, как разработчики Google Play Services решили реализовать его (поэтому я рекомендую вам сообщить об ошибке).

Например, AliExpress каким-то образом справится с этим и перенаправит пользователя на синюю страницу с просьбой снова войти в систему.

Aliexpress использует устаревший API. Как видите, диалоговое окно для выбора аккаунта имеет другой цвет и нет аватаров. API все еще можно использовать, но он может быть отключен в любое время (или нет). Я не рекомендую вам использовать это, но вот как это работает:

import com.google.android.gms.common.AccountPicker;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;

void chooseAccount() {
    Intent signInIntent = AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null);
    startActivityForResult(signInIntent, REQ_CHOOSE_ACCOUNT);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (requestCode == REQ_CHOOSE_ACCOUNT) {

        String email = data.getExtras().getString("authAccount");
        // better do this in background thread
        try {
            GoogleAuthUtil.getToken(this, new Account(email, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE), "oauth2:https://www.googleapis.com/auth/userinfo.profile");
        } catch (UserRecoverableAuthException recEx) {
            Intent recoverIntent = recEx.getIntent();
            // Will redirect to login activity
            startActivityForResult(recoverIntent, REQ_RECOVER);
        } catch (Exception e) {
            Log.d(TAG, "caught exception", e);
        }

    }
}

Надеюсь, поможет!

UPD: новые API Google Play имеют ResolvableApiException, что расширяет ApiException, которую вы ловите. У него есть метод startResolutionForResult(), похожий на тот, который использовался в старых API. Но пакет, который вы получаете, не содержит информации о разрешении.

Bundle[{googleSignInStatus=Status{statusCode=unknown status code: 12501, resolution=null}}]

Если вы сообщите об ошибке, опубликуйте ее здесь, мы отметим ее)

Вы также можете отобразить диалоговое окно "Выбор учетной записи", используя API Android по умолчанию (минимум API 23).

Приведенный ниже код может быть использован для отображения "диалога выбора учетной записи" с использованием стандартных API управления учетными записями Android. Это новое и (надеюсь) не будет устаревшим некоторое время.

import android.accounts.Account;
import android.accounts.AccountManager;

// Unfortunately can be used only on API 23 and higher
Intent signInIntent = AccountManager.newChooseAccountIntent(
            null,
            null,
            new String[] { "com.google" },
            "Please select your account",
            null,
            null,
            new Bundle());

startActivityForResult(signInIntent, REQ_SELECT_ACCOUNT);

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQ_SELECT_ACCOUNT) {
            String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
            String accountType = data.getExtras().getString(AccountManager.KEY_ACCOUNT_TYPE);
            // now you can call GoogleAuthUtil as in example above
        }
    }

Вы также можете получить список учетных записей Google, видимых для вашего приложения

Учетная запись становится видимой для вашего приложения после того, как пользователь попытался войти в приложение с такой учетной записью, используя один из указанных выше способов. Событие, если вход не был успешным (например, истек срок действия пароля), вы увидите эту учетную запись в списке (хотя не сможете определить, какая из них в случае нескольких учетных записей). Так что это может быть использовано в качестве обходного пути, но ограниченным образом.

import android.accounts.Account;
import android.accounts.AccountManager;

try {
        // requires android.permission.GET_ACCOUNTS
        Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
        for (Account account : accounts) {
            Log.d(TAG, "account: " + account.name);
        }
    } catch (Exception e) {
        Log.i("Exception", "Exception:" + e);
    }

Заключение К сожалению, я не нашел других способов доступа к данным аккаунта Google, чтобы обойти ваше дело с помощью современных API входа Google. Все расширенные API AccountManager требуют, чтобы у вас была та же подпись, что и у приложения владельца аккаунта (GMS - Google Mobile Services), что не соответствует действительности. Поэтому мы можем только запросить это у Google и надеяться, что это будет реализовано :(