Подтвердить что ты не робот

Не должен ли Android AccountManager хранить токены OAuth на основе Per-App/UID?

Учетная запись Android AccountManager отображает один и тот же кэшированный токен аутентификации для приложений с разными UID - это безопасно? Он не кажется совместимым с OAuth2, поскольку токены доступа не должны использоваться совместно между разными клиентами.

Фон/Контекст

Я создаю приложение для Android, которое использует OAuth2 для аутентификации/авторизации запросов REST API на мой сервер, который является поставщиком OAuth2. Поскольку приложение является "официальным" приложением (в отличие от стороннего приложения), он считается доверенным клиентом OAuth2, поэтому я использую поток паролей владельца ресурса для получения токена OAuth2: пользователь (владелец ресурса) вводит его имя пользователя/пароль в приложение, которое затем отправляет свой идентификатор клиента и секрет клиента вместе с учетными данными пользователя на конечную точку маркера OAuth2 на сервере в обмен на токен доступа, который может использоваться для совершения вызовов API, обновленный токен, используемый для получения новых токенов доступа, когда они истекают. Обоснованием является то, что более безопасно хранить токен обновления на устройстве, чем пароль пользователя.

Я использую AccountManager для управления учетной записью и ассоциированным токеном доступа на устройстве. Поскольку я предоставляю свой собственный поставщик OAuth2, я создал свой собственный тип учетной записи, расширив AbstractAccountAuthenticator и другие необходимые компоненты, как объяснено в этом руководстве Android Dev Guide и продемонстрирован в примере примера SampleSyncAdapter. Я могу успешно добавлять учетные записи своего пользовательского типа из своего приложения и управлять ими с экрана настроек Android "Учетные записи и синхронизация".

Проблема

Однако я обеспокоен тем, как AccountManager кэширует и выдает токены auth - в частности, что тот же токен аутентификации для данного типа учетной записи и типа токена представляется доступным для любого приложения, которому предоставлен пользователь доступ.

Чтобы получить токен auth через AccountManager, необходимо вызвать AccountManager.getAuthToken(), передав, помимо прочего, Account экземпляр для получения токена аутентификации и желаемого authTokenType. Если для указанной учетной записи и authTokenType существует токен аутентификации, и если пользователь предоставляет доступ (через экран "Access Request" на приложение, которое произвело запрос токена аутентификации (в тех случаях, когда идентификатор запрашивающего приложения не соответствует идентификатору аутентификатора), тогда возвращается токен. В случае отсутствия моего объяснения, эта полезная запись в блоге объясняет это очень четко. На основании этого сообщения и после изучения источника AccountManager и AccountManagerService (внутренний класс, который делает тяжелую работу для AccountManager) для себя, кажется, что только 1 токен аутентификации хранится в каждой команде authTokenType/account.

Итак, представляется возможным, что , если злонамеренное приложение знало тип учетной записи и authTokenType (ы), используемые моим аутентификатором, он мог вызывать AccountManager.getAuthToken(), чтобы получить доступ к моему токену OAuth2, хранящемуся в приложении, предполагая, что пользователь предоставляет доступ к вредоносному приложению.

Для меня проблема заключается в том, что реализация кэширования по умолчанию AccountManager построена на парадигме, на которой, если бы мы должны были наложить аутентификационный/авторизационный контекст OAuth2, он рассмотрел бы телефон/устройство как единственный клиент OAuth2 для службы/поставщик ресурсов. Принимая во внимание, что парадигма, которая имеет для меня смысл, заключается в том, что каждое приложение /UID следует рассматривать как свой собственный клиент OAuth2.Когда мой поставщик OAuth2 выдает токен доступа, он выдает токен доступа для этого конкретного приложения, которое отправило правильный идентификатор клиента и секрет клиента, а не все приложения на устройстве. Например, у пользователя может быть как мое официальное приложение (назовите его app Client A), так и "лицензированное" стороннее приложение, которое использует мой API (назовите приложение "Клиент B" ). Для официального клиента A мой провайдер OAuth2 может выдавать маркер типа "супер" /тип, который предоставляет доступ как к публичным, так и к частным частям моего API, тогда как для стороннего клиента B мой провайдер может выдавать "ограниченный" тип /scope, который предоставляет только доступ к общедоступным API-вызовам. Клиент App B не должен быть доступен для получения приложения. Клиентский токен доступа, который, по-видимому, разрешает текущая реализация AccountManager/AccountManagerService.. Даже если пользователь предоставляет разрешение клиенту B для клиента. A super токен, факт остается фактом: мой поставщик OAuth2 предназначен только для предоставления этого токена клиенту A.

Я что-то забыл? Является ли мое убеждение, что токены auth должны быть выпущены на основе каждого приложения /UID (каждое приложение, являющееся отдельным клиентом) рационально/практично, или являются аутентификационными марками для каждого устройства (каждое устройство является клиентом) стандартным/принятым практика?

Или есть некоторые недостатки в моем понимании ограничений кода/безопасности вокруг AccountManager/AccountManagerService, так что эта уязвимость фактически не существует? Я протестировал вышеупомянутый сценарий клиента A/Client B с помощью AccountManager и моего пользовательского аутентификатора, а мое тестовое клиентское приложение B, которое имеет разную область пакета и UID, смогло получить токен аутентификации, который был выпущен моим сервером для моего тестового клиентского приложения A, передав тот же самый authTokenType (во время которого мне был предложен экран предоставления доступа "Access Request", который я одобрил с тех пор, как я являюсь пользователем и, следовательно, не знаю)...

Возможные решения

а. "Секретный" authTokenType
Чтобы получить токен аутентификации, authTokenType должен быть известен; должен ли authTokenType рассматриваться как тип секретности клиента, так что токен, выданный для данного типа секретного токена, может быть получен только теми "авторизованными" клиентскими приложениями, которые знают секретный токен? Это не кажется очень безопасным; на корневом устройстве можно было бы изучить столбец auth_token_type таблицы authtokens в базе данных accounts системы и проверить значения authTokenType, которые хранятся вместе с моими токенами. Таким образом, "секретные" типы токенов аутентификации, используемые во всех установках моего приложения (и любых разрешенных сторонних приложений, используемых на устройстве), будут отображаться в одном центральном месте. По крайней мере, с идентификаторами/секретами идентификаторов OAuth2, даже если они должны быть упакованы вместе с приложением, они распространяются среди разных клиентских приложений, и можно попытаться запутать их (что лучше, чем ничего), чтобы помочь отпугнуть тех, кто распаковать/декомпилировать приложение.

б. Пользовательские маркеры Auth
Согласно документам для AccountManager.KEY_CALLER_UID и AuthenticatorDescription.customTokens, и исходный код AccountManagerService, на который я ссылался ранее, я должен уметь указать, что мой собственный тип учетной записи использует "пользовательские токены" и включает мою собственную реализацию кэширования/хранения токенов в моем пользовательском аутентификаторе, где я могу получить UID вызывающее приложение в порядке хранения/выборки токенов auth для каждого UID. В принципе, у меня бы была таблица authtokens, такая как реализация по умолчанию, за исключением того, что был добавлен столбец uid, чтобы токены были уникально индексированы по идентификатору UID, учетной записи и Auth Token Type (в отличие от типа учетной записи и Auth Token Type). Это похоже на более безопасное решение, чем использование "секретных" authTokenTypes, поскольку это предполагает использование одного и того же authTokenTypes для всех установок моего приложения/аутентификатора, тогда как UID варьируются от системы к системе и не могут быть легко подделаны. Помимо радостных накладных расходов, связанных с получением и управлением собственным механизмом кэширования токенов, какие недостатки существуют для этого подхода с точки зрения безопасности? Это слишком много? Я действительно что-то защищаю, или мне не хватает чего-то такого, что даже при такой реализации на месте все равно будет достаточно просто, чтобы один клиент вредоносного приложения мог получить еще один токен аутентификации клиента приложения с помощью AccountManager и authTokenType (s), которые не гарантируют себя секретными (если предположить, что указанное вредоносное приложение не знает секрет клиента OAuth2 и поэтому не может напрямую получить новый токен, но может надеяться получить тот, который уже был кэширован в AccountManager от имени авторизованный клиент приложения)?

с. Отправить идентификатор клиента/секретный токен/маркер OAuth2
Я мог бы использовать реализацию хранилища токенов AccountManagerService по умолчанию и принять возможность несанкционированного доступа к токену моего приложения, но я мог заставить запросы API всегда включать идентификатор клиента OAuth2 и секрет клиента, в дополнение к токену доступа, и убедитесь, что на стороне сервера установлено, что приложение является авторизованным клиентом, для которого в первую очередь был выпущен токен. Тем не менее, я хотел бы избежать этого, потому что A) AFAIK, спецификация OAuth2 не требует аутентификации клиента для защищенных запросов ресурсов - требуется только токен доступа и B) Я бы хотел избежать дополнительных накладных расходов на аутентификацию клиента по каждому запросу.

Это невозможно в общем случае (все серверы - это серия сообщений в протоколе - код, сгенерированный этими сообщениями, не может быть определен). - Майкл

Но то же самое можно сказать об аутентификации первоначального клиента в потоке OAuth2, в течение которого клиенту сначала выдают токен доступа. Единственное различие заключается в том, что вместо аутентификации только на токен-запросе запросы на защищенные ресурсы также будут аутентифицироваться одинаково. (Обратите внимание, что клиентское приложение сможет передать свой идентификатор клиента и секрет клиента через параметр loginOptions AccountManager.getAuthToken(), который мой пользовательский аутентификатор просто передаст моему поставщику ресурсов по протоколу OAuth2).


Ключевые вопросы

  • Возможно ли, чтобы одно приложение могло получить другое приложение authToken для учетной записи, вызвав AccountManager.getAuthToken() с тем же authTokenType?
  • Если это возможно, - это действительная/практическая проблема безопасности в контексте OAuth2?

    Вы никогда не сможете полагаться на токен аутентификации, предоставленный пользователю, оставшийся секретным от этого пользователя... поэтому разумно для Android игнорировать эту безопасность по цели безвестности в своем дизайне - Майкл

    НО- Я не беспокоюсь о том, что пользователь (владелец ресурса) получает токен авторизации без моего согласия; Меня беспокоят несанкционированные клиенты (приложения). Если пользователь хочет стать злоумышленником своих защищенных ресурсов, то он может выбить себя. Я говорю, что не должно быть возможным, чтобы пользователь установил мое клиентское приложение и, невольно, клиентское приложение "imposter", которое может получить доступ к токену аутентификации приложения просто потому, что оно передано в правильном authTokenType, и пользователь был слишком ленив/не знал/бросился проверять экран запроса доступа. Эта аналогия может быть немного упрощена, но я не считаю ее "безопасностью по неизвестности", что мое установленное приложение Facebook не может читать электронные письма, кэшированные моим приложением Gmail, которое отличается от меня (пользователя), укореняющего мой телефон и проверяющего кеш содержание.

    Пользователю необходимо принять (доступ к системе Android) запрос доступа для использования вашего токена... Учитывая, что решение для Android кажется ОК - приложения не могут молча использовать аутентификацию пользователя без запроса - Майкл

    НО. Это также проблема авторизации - токен авторизации, выданный для моего "официального" клиента, является ключом к набору защищенных ресурсов, для которых этот клиент и только тот клиент авторизован. Я полагаю, можно утверждать, что, поскольку пользователь является владельцем этих защищенных ресурсов, если он принимает запрос доступа от стороннего клиента (будь то "спрятанное" партнерское приложение или какой-либо фишер), то он фактически разрешает третьим сторонам, который сделал запрос на доступ к этим ресурсам. Но у меня есть проблемы с этим:

    • Средний пользователь недостаточно сознателен, чтобы грамотно принять это решение. Я не думаю, что мы должны полагаться исключительно на суждение пользователя, чтобы нажать "Отклонить" на экране запроса на Android, чтобы предотвратить даже грубую попытку фишинга. Когда пользователю предоставляется запрос на доступ, мой аутентификатор может быть очень подробным и перечислять все типы чувствительных защищенных ресурсов (к которым должен иметь доступ только мой клиент), для которых пользователь будет предоставлять, если он примет запрос, и в большинстве случаев пользователь все равно будет слишком не осведомлен и собирается принять. И в других, более сложных попытках фишинга, приложение "imposter" просто будет выглядеть слишком "официальным", чтобы пользователь даже поднял бровь на экране запроса доступа. Или, здесь более тупой пример - на экране запроса доступа мой аутентификатор может просто сказать: "Не принимайте этот запрос! Если вы видите этот экран, злоумышленное приложение пытается получить доступ к вашей учетной записи!" Надеюсь, в таком случае большинство пользователей отклонят запрос. Но... почему он должен так далеко? Если Android просто сохранил токены авторизации, выделенные для каждого приложения /UID, для которого они были выпущены, тогда это будет не проблема. Пусть упрощается - даже в случае, когда у меня есть только одно "официальное" клиентское приложение, и поэтому мой поставщик ресурсов даже не беспокоится о выдаче токенов другим, сторонним клиентам, как разработчику, я должен иметь возможность сказать AccountManager, "Нет! Блокируйте этот токен авторизации, чтобы доступ только к моему приложению". Я могу сделать это, если я пойду по маршруту "пользовательских токенов", но даже в этом случае я не смогу помешать пользователю сначала предоставить экран запроса доступа. По крайней мере, должно быть лучше документировано, что реализация AccountManager.getAuthToken() по умолчанию возвращает один и тот же токен аутентификации для всех запрашивающих приложений /UID.
    • Даже Android-устройства распознают OAuth2 как "отраслевой стандарт "для аутентификации (и, предположительно, авторизации). Спецификация OAuth2 четко указывает, что токены доступа не должны делиться между клиентами или разглашать каким-либо образом. Почему же тогда реализация/конфигурация AccountManager по умолчанию упрощает клиенту получение того же кэшевого токена аутентификации, который был первоначально получен из службы другим клиентом? Простое исправление в AccountManager будет состоять только в повторном использовании кэшированных токенов для того же приложения /UID, под которым они были первоначально получены из службы. Если для данного UID не существует локального кэшированного токена аутентификации, он должен быть получен из службы. Или, по крайней мере, сделать это настраиваемым вариантом для разработчика.
    • В потоке OAuth 3-legged (который включает пользователя, предоставляющего доступ клиенту), не предполагается, что он является поставщиком услуг/ресурсов (а не, скажем, ОС), который получает A ) аутентифицировать клиента и B), если клиент действителен, предоставить пользователю запрос доступа к гранту? Похоже, что Android (неправильно) узурпирует эту роль в потоке.

    Но пользователь может явно разрешить приложениям повторно использовать предыдущую аутентификацию для службы, которая удобна для пользователя.-- Michael

    НО. Я не думаю, что рентабельность инвестиций оправдывает угрозу безопасности. В случаях, когда пароль пользователя хранится в учетной записи пользователя, действительно, единственное удобство, которое покупается для пользователя, заключается в том, что вместо отправки веб-запроса моей службе, чтобы получить новый, отличный токен, который фактически разрешен для запрашивающий клиент, локально кэшированный токен, не авторизированный для клиента, возвращается. Таким образом, пользователь получает небольшое удобство видеть диалог "Подпись в..." на пару секунд меньше, рискуя стать причиной серьезного неудобства пользователя, если его ресурсы будут украдены/использованы неправильно.

  • Помня о том, что я привержен A), используя протокол OAuth2 для защиты моих запросов API, B), предоставляя мой собственный поставщик ресурсов/аутентификации OAuth2 (в отличие от аутентификации, скажем, Google или Facebook) и C), используя Android AccountManager для управления моим настраиваемым типом учетной записи и ее токенами, любое из моих предложенных решений действительно? Какой смысл? Могу ли я игнорировать любые плюсы и минусы? Есть ли полезные альтернативы, о которых я не думал?

    [Использовать] Альтернативные клиенты У вас нет секретного API, который пытается быть доступен только для официального клиента; люди обойдутся этим. Убедитесь, что все ваши общедоступные API-интерфейсы защищены независимо от того, какой (будущий) клиент использует - Michael

    НО. Разве это не побеждает одну из ключевых целей использования OAuth2 в первую очередь? Какая польза от авторизации, если все потенциальные авторизованные полномочия будут разрешены к той же области защищенных ресурсов?

  • Кто-нибудь еще почувствовал, что это проблема, и как ваша работа вокруг нее?. Я сделал несколько обширных Googling, чтобы попытаться найти, считают ли другие, что это безопасность проблема/проблема, но, похоже, что большинство сообщений/вопросов, связанных с Android AccountManager и токенами аутентификации, - это то, как аутентифицироваться с учетной записью Google, а не с настраиваемым типом учетной записи и поставщиком OAuth2. Более того, я не мог найти никого, кто соглашался бы на возможность того, что один и тот же токен аутентификации используется разными приложениями, что заставляет меня задаться вопросом, действительно ли это действительно/заслуживает внимания (см. Мои первые 2 "Основные вопросы" ", перечисленные выше).

Я ценю ваш ввод/руководство!


В ответ на...

Michael Answer. Я думаю, что основные трудности, которые я испытываю с вашим ответом:

  • Я все еще склонен думать о том, что приложения являются отдельными отдельными клиентами службы, в отличие от самого пользователя/телефона/устройства, являющегося одним "большим" клиентом, и, следовательно, токена, который был разрешен для приложений одно приложение не должно по умолчанию переноситься на тот, у которого его нет. Похоже, вы можете намекнуть, что обсуждать каждое приложение как отдельный клиент из-за возможности того, что

    пользователь может работать с корневым телефоном и считывать токен, получая доступ к вашему частному API... [или], если система пользователя была взломана (злоумышленник мог в этом случае прочитать токен)

    и поэтому, по большому счету, мы должны рассматривать устройство как клиент сервиса, поскольку мы не можем гарантировать безопасность между приложениями на самом устройстве. Это правда, если сама система была скомпрометирована, тогда не может быть гарантии аутентификации/авторизации запросов, отправляемых с этого устройства на службу. Но то же самое можно сказать, скажем, TLS; безопасность на транспорте не имеет значения, если сами конечные точки не могут быть защищены. И для подавляющего большинства устройств Android, которые не подвержены риску, я считаю, что более безопасно рассматривать каждый клиент приложения как отдельную конечную точку, вместо того, чтобы сбрасывать их все в один, разделяя один и тот же токен аутентификации.

  • При представлении экрана "запрос на доступ" (аналогично лицензионному соглашению пользователя программного обеспечения, которое мы всегда тщательно читаем перед согласием и установкой), я не доверяю суждению пользователя, чтобы отличить вредоносное/неавторизованное клиентское приложение от того, которое нет.
4b9b3361

Ответ 1

Является ли это актуальной/практической проблемой безопасности?

Для официального клиента A мой провайдер OAuth2 может выпустить маркер типа "супер" /область доступа, который предоставляет доступ как к публичным, так и к частным частям моего API

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

Другими словами, нет никакой API-интерфейса "private", которая одновременно доступна любому аутентифицированному пользователю, поэтому разумно, чтобы Android игнорировал эту безопасность по цели безвестности в своем дизайне.

вредоносное приложение... может получить доступ к моему программному маркеру OAuth2, хранящемуся в приложении

В случае с вредоносным приложением становится более разумным, чтобы вредоносное приложение не могло использовать токен клиента, так как мы ожидаем, что система разрешений на Android обеспечит изоляцию вредоносных приложений (при условии, что пользователь будет читать/заботиться о разрешения, которые они приняли при их установке). Однако, как вы говорите, пользователю необходимо принять запрос доступа (приложение для Android), чтобы приложение использовало ваш токен.

Учитывая, что решение для Android кажется ОК - приложения не могут молча использовать аутентификацию пользователя без запроса, но пользователь может явно разрешить приложениям повторно использовать предыдущую аутентификацию для службы, которая удобна для пользователя.

Обзор возможных решений

"Секретный" authTokenType... не кажется очень безопасным

Согласовано - это просто еще один уровень безопасности через неясность; это похоже на то, что любое приложение, желающее поделиться своей аутентификацией, должно было искать, что такое authTokenType, так что принятие этого подхода просто делает его немного более неудобным для этого гипотетического разработчика приложений.

Отправить идентификатор клиента/секретный токен/маркер OAuth2... [проверить] на стороне сервера, что приложение является авторизованным клиентом

Это невозможно в общем случае (все серверы - это серия сообщений в протоколе - код, сгенерированный этими сообщениями, не может быть определен). В этом конкретном случае он может защитить от более ограниченной угрозы (не root) альтернативного клиента/вредоносного приложения - я недостаточно разбираюсь в том, что AccountManager комментирует (так же, как и ваши пользовательские решения tohens auth).

Предложение

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

  • Вредоносные приложения: подумайте, насколько важна служба, которую вы предоставляете, и если она не более чувствительна, чем, например, Google/twitter, просто полагайтесь на защиту Android (разрешения на установку, экран запроса доступа). Если это более чувствительно, подумайте, подходит ли вам ограничение использования Android AccountManager. Чтобы сильно защитить пользователя от вредоносного использования своей учетной записи, попробуйте выполнить двухфакторную аутентификацию для опасных действий (c. F. Добавление новых данных учетной записи получателя в онлайн-банкинг).

  • Альтернативные клиенты: не имеют секретного API, который пытается быть доступен только для официального клиента; люди обойдутся этим. Убедитесь, что все ваши публичные API-интерфейсы защищены независимо от того, какой (будущий) клиент использует.

Ответ 2

Ваше наблюдение верное. Аутентификатор будет работать с тем же UID, что и установочное приложение. Когда другое приложение подключается к диспетчеру аккаунта и получает токен для этого аутентификатора, он будет привязываться к предоставленной вами службе аутентификации. Он будет работать как ваш UID, поэтому новые учетные записи будут связаны с этим Authenticator. Когда приложение вызывает getAuthToken, привязка произойдет, и Authenticator все равно будет работать в том же UId. По умолчанию встроенная проверка разрешений для UID учетной записи, так что другой Authenticator не смог получить доступ к другой учетной записи из другого Authenticator.

Вы можете решить эту проблему с помощью "Call UID" для addAccount и GetAuthToken, поскольку служба диспетчера учетных записей добавляет, что для объединения. Реализация аутентификатора может проверить это.

   @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
            String authTokenType, Bundle loginOptions) throws NetworkErrorException {
        Log.v(
                TAG,
                "getAuthToken() for accountType:" + authTokenType + " package:"
                        + mContext.getPackageName() + "running pid:" + Binder.getCallingPid()
                        + " running uid:" + Binder.getCallingUid() + " caller uid:"
                        + loginOptions.getInt(AccountManager.KEY_CALLER_UID));
          ...
}

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

Когда вы добавляете учетную запись, вы также можете запросить callUId. Вам нужно setUserData​​strong > в вашей активности addAccount, которая будет работать как UID вашего приложения, поэтому она может вызывать setUserData​​strong > .

getUserData​​strong > и setUserData​​strong > использует встроенную базу данных sqlite, поэтому вам не нужно самостоятельно создавать кеш. Вы можете хранить только строковый тип, но вы можете анализировать json и хранить дополнительную информацию для каждой учетной записи.

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

Разделение токенов между приложениями: Каждое приложение обычно регистрируется с разными клиентами, и они не должны использовать токен. Токен для клиентского приложения.

хранения: AccountManager не шифрует ваши данные. Если вам нужно более безопасное решение, вы должны зашифровать токены, а затем сохранить его.

Ответ 3

Я столкнулся с одной и той же архитектурной проблемой для приложения.

Решение, которое я получил, это связать/хеш маркер oauth с токеном поставщика приложений (например, токеном, который дает facebook для приложения), и идентификатором устройства (android_id). Таким образом, разрешено только приложение, так как устройство может использовать токен у менеджера аккаунта.

Конечно, это просто новый уровень безопасности, но без доказательства пули.

Ответ 4

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

Ваша озабоченность по поводу безопасности android AccountManager правильная, но это то, о чем должен думать OAuth, на что полагается android AccountManager.

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

Лучшая альтернатива OAuth в более безопасных системах аутентификации, например. онлайн-банкинга, использует асимметричное шифрование с использованием открытых и закрытых ключей, в которых пользователю требуется каждый раз вводить свой пароль для использования этих сервисов. Затем пароль шифруется с использованием открытого ключа на устройстве и отправляется на сервер. Здесь, даже если злоумышленник узнает о зашифрованном пароле, он ничего не может с этим поделать, потому что он не может расшифровать его с помощью этого открытого ключа и ему нужен только закрытый ключ сервера.

В любом случае, если вы хотите использовать систему AccountManager андроида, а также поддерживать высокий уровень безопасности, это было бы возможно, не сохраняя никаких токенов на устройстве. Метод getAuthToken из AbstractAccountAuthenticator можно затем переопределить следующим образом:

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String
        authTokenType, Bundle options) throws NetworkErrorException {
    AuthenticatorManager authenticatorManager = AuthenticatorManager.authenticatorManager;
    Bundle result;
    AccountManager accountManager = AccountManager.get(context);
    // case 1: access token is available
    result = authenticatorManager.getAccessTokenFromCache(account, authTokenType,
            accountManager);
    if (result != null) {
        return result;
    }
    final String refreshToken = accountManager.getPassword(account);
    // case 2: access token is not available but refresh token is
    if (refreshToken != null) {
        result = authenticatorManager.makeResultBundle(account, refreshToken, null);
        return result;
    }
    // case 3: neither tokens is available but the account exists
    if (isAccountAvailable(account, accountManager)) {
        result = authenticatorManager.makeResultBundle(account, null, null);
        return result;
    }
    // case 4: account does not exist
    return new Bundle();
}

В этом методе ни один случай 1, случай 2 и случай 4 не выполняется, потому что нет сохраненного токена, хотя существует account. Поэтому возвращается только случай 3, который затем может быть установлен в соответствующем обратном вызове, чтобы открыть Activity, в котором пользователь вводит имя пользователя и пароль для аутентификации.

Я не уверен, что буду на правильном пути в дальнейшем описывать это здесь, но мои сообщения на сайте AccountManager могут помочь просто в случае.