【问题标题】:Shouldn't Android AccountManager Store OAuth Tokens on a Per-App/UID Basis?Android AccountManager 不应该基于每个应用程序/UID 存储 OAuth 令牌吗?
【发布时间】:2018-08-03 18:16:40
【问题描述】:

Android 的 AccountManager 似乎为具有不同 UID 的应用获取相同的缓存身份验证令牌 - 这安全吗?它似乎与 OAuth2 不兼容,因为不应该在不同客户端之间共享访问令牌。

背景/上下文

我正在构建一个 Android 应用程序,它使用 OAuth2 对我的服务器(一个 OAuth2 提供程序)的 REST API 请求进行身份验证/授权。由于该应用程序是“官方”应用程序(相对于第三方应用程序),它被认为是受信任的 OAuth2 客户端,因此我使用资源所有者密码流程来获取 OAuth2 令牌:用户(资源所有者)将他的用户名/密码输入应用程序,然后将其客户端 ID 和客户端密码以及用户凭据发送到我的服务器的 OAuth2 令牌端点,以换取可用于进行 API 调用的访问令牌,以及长- live refresh token 用于在过期时获取新的访问令牌。理由是在设备上存储刷新令牌比用户密码更安全。

我正在使用AccountManager 来管理设备上的帐户和关联的访问令牌。由于我提供了自己的 OAuth2 提供程序,因此我通过扩展 AbstractAccountAuthenticator 和其他必需的组件创建了自己的自定义帐户类型,如 in this Android Dev Guide 所述并在 SampleSyncAdapter 示例项目中进行了演示。我能够从我的应用中成功添加我的自定义类型的帐户,并从“帐户和同步”Android 设置屏幕管理它们。

问题

但是,我担心 AccountManager 缓存和发布身份验证令牌的方式 - 具体来说,用户授予的任何应用程序似乎都可以访问给定帐户类型和令牌类型的相同身份验证令牌访问。

要通过 AccountManager 获取身份验证令牌,必须调用 AccountManager.getAuthToken(),其中传递获取身份验证令牌的 Account 实例和所需的 authTokenType。如果指定帐户和 authTokenType 存在 auth 令牌,并且用户授予对已发出 auth 令牌请求的应用程序的访问权限(通过 grant "Access Request" 屏幕)(在这种情况下,请求应用程序的 UID 与验证者的 UID),然后返回令牌。如果缺少我的解释,this helpful blog entry 解释得很清楚。根据该帖子,在为我自己检查了AccountManagerAccountManagerService(为 AccountManager 完成繁重工作的内部类)的来源之后,似乎每个 authTokenType/帐户组合只存储了 1 个身份验证令牌.

因此,如果恶意应用知道我的身份验证器使用的帐户类型和 authTokenType(s),它可以调用 AccountManager.getAuthToken() 来访问我的应用存储的 OAuth2 令牌,这似乎是可行的, 假设用户授予对恶意应用的访问权限。

对我来说,问题在于 AccountManager 的默认缓存实现是建立在一个范式之上的,如果我们将 OAuth2 身份验证/授权上下文分层,它会将电话/设备视为服务的单个 OAuth2 客户端/资源提供者。 然而,对我来说有意义的范例是每个应用程序/UID 都应被视为其自己的 OAuth2 客户端。当我的 OAuth2 提供程序发出访问令牌时,它正在为该特定应用程序发出访问令牌它发送了正确的客户端 ID 和客户端密码,而不是设备上的所有应用程序。例如,用户可能同时安装了我的官方应用程序(称为应用程序客户端 A)和使用我的 API 的“许可”第三方应用程序(称为应用程序客户端 B)。对于官方客户端 A,我的 OAuth2 提供者可能会发布一个“超级”类型/范围令牌,它授予对我的 API 的公共和私有部分的访问权限,而对于第三方客户端 B,我的提供者可能会发布一个“受限”类型/scope 令牌,仅授予对公共 API 调用的访问权限。 应用客户端 B 应该不可能获得应用客户端 A 的访问令牌,当前 AccountManager/AccountManagerService 实现似乎允许这样做。因为,即使用户为客户端 A 的超级用户授予客户端 B 的授权令牌,事实仍然是我的 OAuth2 提供者只打算将该令牌授予客户端 A。

我在这里忽略了什么吗?我认为应该基于每个应用程序/UID(每个应用程序是一个不同的客户端)发布身份验证令牌是合理/实用的,还是每个设备的身份验证令牌(每个设备都是客户端)是标准/接受的练习?

或者我对AccountManager/AccountManagerService 周围的代码/安全限制的理解存在一些缺陷,以至于这个漏洞实际上并不存在?我已经使用AccountManager 和我的自定义身份验证器测试了上述客户端 A/客户端 B 场景,并且我的测试客户端应用程序 B 具有不同的包范围和 UID,能够获取我的服务器发出的身份验证令牌为我的测试客户端应用程序 A 通过传入相同的 authTokenType (在此期间我被提示“访问请求”授予屏幕,我批准了,因为我是用户,因此一无所知)...

可能的解决方案

一个。 “秘密” authTokenType
为了获得授权令牌,authTokenType 必须是已知的; authTokenType 是否应该被视为一种客户端机密,以便为给定机密令牌类型颁发的令牌只能由那些知道机密令牌类型的“授权”客户端应用程序获得?这似乎不太安全;在有根设备上,可以检查系统authtokens 数据库中authtokens 表的auth_token_type 列,并检查与我的令牌一起存储的authTokenType 值。因此,在我的应用程序(以及设备上使用的任何授权第三方应用程序)的所有安装中使用的“秘密”身份验证令牌类型将在一个中心位置公开。至少对于 OAuth2 客户端 ID/秘密,即使它们必须与应用程序一起打包,它们也会分散在不同的客户端应用程序中,并且可能会尝试混淆它们(这总比没有好)以帮助阻止那些会解包/反编译应用程序。

b.自定义身份验证令牌
根据AccountManager.KEY_CALLER_UIDAuthenticatorDescription.customTokens 的文档以及我之前引用的AccountManagerService 源代码,我应该能够指定我的自定义帐户类型使用“自定义令牌”并在其中旋转我自己的令牌缓存/存储实现我的自定义身份验证器,其中我可以获取调用应用程序的 UID,以便按 UID 存储/获取身份验证令牌。 Basically, I would have an authtokens table like the default implementation, except there would be an added uid column so that tokens are uniquely indexed on U̲I̲D̲, a̲c̲c̲o̲u̲n̲t̲, and A̲u̲t̲h̲ ̲T̲o̲k̲e̲n̲ ̲T̲y̲p̲e̲ (as opposed to just a̲c̲c̲o̲u̲n̲t̲ and A̲u̲t̲h̲ ̲T̲o̲k̲e̲n̲ ̲T̲y̲p̲e̲ )。这似乎是比使用“秘密”authTokenTypes 更安全的解决方案,因为这将涉及在我的应用程序/身份验证器的所有安装中使用相同的authTokenTypes,而 UID 因系统而异,并且不容易被欺骗。除了编写和管理我自己的令牌缓存机制带来的快乐开销之外,这种方法在安全性方面还有哪些缺点?是不是矫枉过正?我是否真的在保护任何东西,或者我是否遗漏了一些东西,即使有了这样的实现,一个恶意应用程序客户端仍然很容易使用AccountManagerauthTokenType(s ) 不能保证是秘密的(假设所述恶意应用程序不知道 OAuth2 客户端秘密,因此无法直接获取新令牌,而只能希望获得一个已经缓存在 AccountManager 中的令牌。授权的应用程序客户端)?

c.使用 OAuth2 令牌发送客户端 ID/秘密
我可以坚持使用AccountManagerService 的默认令牌存储实现并接受未经授权访问我的应用程序的身份验证令牌的可能性,但我可以强制 API 请求始终包含 OAuth2 客户端 ID 和客户端密码,以及访问令牌,并在服务器端验证该应用程序是首先为其颁发令牌的授权客户端。但是,我想避免这种情况,因为 A) AFAIK,OAuth2 规范不需要对受保护的资源请求进行客户端身份验证 - 只需要访问令牌,并且 B)我想避免在每个请求上验证客户端的额外开销。

这在一般情况下是不可能的(服务器获得的只是协议中的一系列消息 - 无法确定生成这些消息的代码)。 --Michael

但是对于 OAuth2 流程中的初始客户端身份验证也可以这样说,在此期间客户端首次获得访问令牌。唯一的区别是,不仅对令牌请求进行身份验证,对受保护资源的请求也将以相同的方式进行身份验证。 (请注意,客户端应用程序将能够通过 AccountManager.getAuthToken()loginOptions 参数传递其 c̲l̲i̲e̲n̲t̲ ̲i̲d̲ 和 c̲l̲i̲e̲n̲t̲ ̲s̲e̲c̲r̲e̲t̲,根据 OAuth2 协议,我的自定义身份验证器只会将其传递给我的资源提供者。


关键问题

  1. 一个应用是否确实可以通过调用具有相同 authTokenType 的 AccountManager.getAuthToken() 来获取另一个应用的 authToken
  2. 如果可能,这是 OAuth2 上下文中有效/实际的安全问题吗?

    您永远不能依赖提供给用户的身份验证令牌来保留该用户的秘密......因此,Android 在其设计中通过模糊目标忽略这种安全性是合理的 --Michael

    但是 - 我不担心用户(资源所有者)在未经我同意的情况下获取身份验证令牌;我担心未经授权的客户(应用程序)。如果用户想成为自己受保护资源的攻击者,那么他可以将自己击倒。我是说用户不可能安装我的客户端应用程序,并且无意中安装了一个“冒名顶替”客户端应用程序,它能够访问我的应用程序的身份验证令牌,仅仅是因为它传入了正确的 authTokenType 并且用户是太懒/不知道/急于检查访问请求屏幕。这个类比可能有点过于简单了,但我不认为我安装的 Facebook 应用程序无法读取我的 Gmail 应用程序缓存的电子邮件是“默默无闻的安全”,这与我(用户)对我的手机进行 root 并检查缓存不同我自己的内容。

    用户需要接受(Android 系统提供的)应用访问请求才能使用您的令牌...鉴于此,Android 解决方案似乎还可以 - 应用无法在不询问的情况下静默使用用户的身份验证 --@987654335 @

    但是 - 这也是授权的问题 - 为我的“官方”客户端颁发的身份验证令牌是该客户端所使用的一组受保护资源的关键只有那个客户是被授权的。我想有人可能会争辩说,由于用户是这些受保护资源的所有者,如果他接受来自第三方客户端的访问请求(无论是“受制裁的”合作伙伴应用程序还是某些网络钓鱼者),那么他实际上是在授权第三方-请求访问这些资源的一方客户端。但我对此有疑问:

    • 普通用户没有足够的安全意识,无法胜任做出此决定。我不认为我们应该仅仅依靠用户的判断来点击 Android 的访问请求屏幕上的“拒绝”来防止即使是粗暴的网络钓鱼尝试。当用户收到访问请求时,我的身份验证器可以非常详细并枚举所有类型的敏感受保护资源(只有我的客户应该能够访问),如果他接受请求,用户将授予这些资源,并且在大多数情况下,用户仍然不会意识到并且会接受。在其他更复杂的网络钓鱼尝试中,“冒名顶替”应用程序看起来太“官方”了,用户甚至无法在访问请求屏幕上挑眉。或者,这里有一个更直接的例子——在访问请求屏幕上,我的身份验证器可以简单地说,“不要接受这个请求!如果你看到这个屏幕,恶意应用程序正试图访问你的帐户!” 希望在这种情况下,大多数用户会拒绝该请求。但是 - 为什么它甚至应该走那么远?如果 Android 只是将身份验证令牌与为其颁发的每个应用程序/UID 的范围隔离,那么这将不是问题。让我们简化一下——即使我只有一个“官方”客户端应用程序,因此我的资源提供者甚至不担心向其他第三方客户端发布令牌,作为开发人员,我应该可以选择对AccountManager,“不!锁定此身份验证令牌,以便只有我的应用程序可以访问。”如果我沿着“自定义令牌”路线走,我可以做到这一点,但即使在这种情况下,我也无法阻止用户首先看到访问请求屏幕。至少,应该更好地记录 AccountManager.getAuthToken() 的默认实现将为所有请求的应用程序/UID 返回相同的身份验证令牌。
    • 甚至 Android 文档都将 OAuth2 识别为“industry standard”用于身份验证(并且可能是授权)。 OAuth2 规范明确指出,访问令牌不得在客户端之间共享或以任何方式泄露。那么,为什么默认的 AccountManager 实现/配置使客户端能够如此轻松地获得与另一个客户端最初从服务获得的相同的缓存身份验证令牌? AccountManager 中的一个简单修复是仅将缓存的令牌重新用于相同的应用程序/UID,它们最初是从服务中获取的。如果给定的 UID 没有本地缓存​​的身份验证令牌,则应从服务中获取。或者至少让它成为开发人员的可配置选项。
    • 在 OAuth 3-legged 流程(涉及用户授予对客户端的访问权限)中,它不应该是到达 A 的服务/资源提供者(而不是操作系统)吗? ) 验证客户端和B) 如果 客户端是有效的,向用户提供授予访问请求?似乎 Android (错误地)在流程中篡夺了这个角色。

    但用户可以显式允许应用重新使用之前对服务的身份验证,这对用户来说很方便。--Michael

    但是 - 我不认为便利的投资回报率保证安全风险。如果用户的密码存储在用户的帐户中,那么实际上,为用户购买的唯一便利是,无需向我的服务发送 Web 请求以获取实际授权的新的、不同的令牌请求客户端,返回一个本地缓存的未授权给客户端的令牌。因此,用户可以在几秒钟内看到“正在登录...”进度对话框,从而获得一点便利,但用户可能会因资源被盗/滥用而造成极大的不便。

  3. 请记住,我致力于 A) 使用 OAuth2 协议来保护我的 API 请求,B) 提供我自己的 OAuth2 资源/身份验证提供程序(相反例如,通过 Google 或 Facebook 进行身份验证)以及 C) 利用 Android 的 AccountManager 管理我的自定义帐户类型及其令牌,我提出的任何解决方案是否有效? 哪个最有意义?我是否忽略了任何优点/缺点?有没有我没有想到的有价值的替代方案?

    [使用] 替代客户端 没有试图仅由官方客户端访问的秘密 API;人们会绕过这个。无论用户使用什么(未来)客户端,确保所有面向公众的 API 都是安全的 --Michael

    但是 - 这不是首先破坏了使用 OAuth2 的主要目的之一吗?如果所有潜在的被授权者都被授权使用相同范围的受保护资源,那么授权有什么用?

  4. 有没有其他人认为这是一个问题,您是如何解决这个问题的?我做了一些广泛的谷歌搜索,试图找出其他人是否认为这是一个安全问题问题/关注,但似乎大多数涉及 Android 的 AccountManager 和身份验证令牌的帖子/问题都是关于如何使用 Google 帐户进行身份验证,而不是使用自定义帐户类型和 OAuth2 提供程序。此外,我找不到任何人担心不同应用程序使用相同的身份验证令牌的可能性,这让我想知道这是否确实是一种可能性/值得关注(请参阅我的前 2 个“关键问题” 上面列出的)。

感谢您的意见/指导!


回应...

Michael's Answer - 我认为我对您的回答的主要困难是:

  1. 我仍然倾向于将应用程序视为服务的独立、不同的客户端,而不是用户/电话/设备本身是一个“大”客户端,因此已为一个应用程序授权的令牌应该默认情况下,不可转让给尚未转让的人。您似乎在暗示将每个应用程序视为不同的客户端是没有实际意义的,因为有可能,

    用户可能正在运行有根手机,并读取令牌,获得对您的私有 API 的访问权限... [或] 如果用户的系统受到威胁(在这种情况下,攻击者可以读取令牌)

    因此,从总体上看,我们应该将设备视为服务的客户端,因为我们无法保证设备本身上的应用程序之间的安全性。如果系统本身已被入侵,则无法保证从该设备向服务发送验证/授权请求。但同样可以说,比如 TLS;如果端点本身不能得到保护,则传输安全是无关紧要的。对于绝大多数没有受到攻击的 Android 设备,我认为将每个应用程序客户端视为一个不同的端点会更安全,而不是通过共享相同的身份验证令牌将它们全部归为一个。

  2. 当出现“访问请求”屏幕时(类似于我们在同意和安装之前始终通读的软件用户许可协议),我不相信用户能够区分恶意/未经授权的客户端应用程序和不是。

【问题讨论】:

  • +1 这是我最近在 SO 上看到的最好的问题之一。
  • 顺便说一句,Oauth2 不建议使用 client_secret 从本机应用程序请求令牌,因为与仅使用 client_id 相比,它没有增加任何价值。本机应用程序不是机密客户端,无法保护适当的机密;任何非官方应用程序的开发者都可以在他的软件中使用相同的秘密。这个问题在this answerOAuth 2.0 for Native Apps 中有解释

标签: android security authentication oauth accountmanager


【解决方案1】:

这是一个有效/实际的安全问题吗?

对于官方客户端 A,我的 OAuth2 提供者可能会发布一个“超级”类型/范围令牌,授予对我的 API 的公共和私有部分的访问权限

在一般情况下,您永远不能依赖提供给用户的身份验证令牌来自该用户。例如 - 用户可能正在运行有根手机,并读取令牌,获得对您的私有 API 的访问权限。如果用户的系统被攻破(在这种情况下攻击者可以读取令牌),则同理。

换句话说,没有任何经过身份验证的用户可以同时访问的“私有”API 这样的东西,因此 Android 在其设计中忽略这种安全性目标是合理的。

恶意应用...可以访问我的应用存储的 OAuth2 令牌

对于恶意应用案例,恶意应用不应该能够使用客户端的令牌听起来更合理,因为我们希望 Android 的权限系统能够提供对恶意应用的隔离(前提是用户阅读/关心他们安装时接受的权限)。但是,正如您所说,用户需要接受(Android 系统提供的)应用访问请求才能使用您的令牌。

鉴于此,Android 解决方案似乎还可以 - 应用程序不能在不询问的情况下静默使用用户的身份验证,但用户可以明确允许应用程序重新使用以前的服务身份验证,这对用户来说很方便。

可能的解决方案审查

"Secret" authTokenType ... 似乎不太安全

同意 - 这只是通过默默无闻的另一层安全;听起来任何希望共享您的身份验证的应用程序都必须查找 authTokenType 是什么,所以采用这种方法只会让这个假设的应用程序开发人员更加尴尬。

使用 OAuth2 令牌发送客户端 ID/秘密 ... [以] 验证服务器端应用程序是授权客户端

这在一般情况下是不可能的(服务器获得的只是协议中的一系列消息 - 无法确定生成这些消息的代码)。在这种特定情况下,它可以防止(非根)替代客户端/恶意应用程序的更有限的威胁 - 我对 AccountManager 不够熟悉,无法发表评论(同样适用于您的自定义身份验证令牌解决方案)。

建议

您描述了两种威胁 - 用户不希望访问其帐户的恶意应用,以及您(开发人员)不希望使用部分 API 的替代客户端。

  • 恶意应用程序:考虑您提供的服务有多敏感,以及它是否不比例如谷歌/推特账户,只依赖安卓的保护(安装权限,访问请求屏幕)。如果 更敏感,请考虑您使用 Android 的 AccountManager 的限制是否合适。为了有力地保护用户免受恶意使用其帐户的影响,请尝试对危险操作进行双重身份验证(参见在网上银行中添加新收款人的帐户详细信息)。

  • 替代客户端:没有试图仅由官方客户端访问的秘密 API;人们会绕过这个。无论用户使用什么(未来)客户端,都要确保所有面向公众的 API 都是安全的。

【讨论】:

  • 迈克尔 - 谢谢你的回答!为了帮助可能关注/遇到此问题的其他人,并且由于 cmets 不支持块引用和冗长的回复,我编辑了我的帖子以反映您的想法和建议以及我的回复。另外,如果我误解了您答案的任何部分,或者您觉得我在阅读/断章取义,请不要犹豫,让我知道。再次感谢您花时间、思考和努力回答我的问题!
【解决方案2】:

你的观察是正确的。 Authenticator 将使用与安装应用程序相同的 UID 运行。当另一个应用程序连接到 Account manager 并获取此身份验证器的令牌时,它将绑定到您提供的身份验证器服务。它将作为您的 UID 运行,因此新帐户将与此 Authenticator 相关联。当应用程序调用 getAuthToken 时,将发生绑定并且 Authenticator 仍将在相同的 UId 中运行。默认内置对账号UID的权限检查,使得不同的Authenticator不能访问不同Authenticator的另一个账号。

您可以通过对 addAccount 和 GetAuthToken 使用“调用 UID”来解决此问题,因为客户经理服务会将其添加到捆绑包中。您的身份验证器实现可以检查这一点。

   @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));
          ...
}

我建议遵循授权流程,而不是将客户端密码存储在您的原生应用中,因为其他开发人员可以提取该密码。你的应用不是网络应用,不应该有秘密。

添加账号时,也可以查询callingUId。您需要在您的 addAccount 相关活动中setUserData,该活动将作为您应用的 UID 运行,以便它可以调用 setUserData

getUserDatasetUserData 使用内置的 sqllite 数据库,因此您不需要自己构建缓存。您只能存储字符串类型,但您可以解析 json 并存储每个帐户的额外信息。

当不同的第三方应用查询账号并使用你的账号调用getAuthtoken时,你可以在账号的userdata中查看UID。如果未列出调用 UID,您可以执行提示和/或其他操作以获得许可。如果允许,您可以为帐户添加新的 UID。

在应用之间共享令牌: 每个应用程序通常使用不同的 clientid 注册,并且它们不应共享令牌。令牌用于客户端应用程序。

存储: AccountManager 没有加密您的数据。如果您需要更安全的解决方案,您应该加密令牌然后存储它。

【讨论】:

    【解决方案3】:

    我面临着同样的应用架构问题。

    我得到的解决方案是将 oauth 令牌与应用程序供应商令牌(例如 facebook 提供给应用程序的令牌)以及设备 ID (android_id) 关联/散列。因此,只有获得授权的应用程序才能使用来自客户经理的令牌。

    当然,这只是一个新的安全层,但不是防弹的。

    【讨论】:

      【解决方案4】:

      我认为@Michael 完美地回答了这个问题;但是,为了让那些寻求快速答案的人更明智、更简短,我正在写这篇文章。

      您对 android AccountManager 安全性的担忧是正确的,但这就是 OAuth 的意义所在,而 android AccountManager 则依赖于此。

      换句话说,如果您正在寻找一种非常安全的身份验证机制,这对您来说不是一个好的选择。您不应该依赖任何缓存的令牌进行身份验证,因为如果用户设备上存在任何安全漏洞,例如无意中授予入侵者访问权限、运行 root 设备等,它们很容易被泄露给入侵者。

      在更安全的身份验证系统中,OAuth 的更好替代方案,例如网上银行应用程序正在使用使用公钥和私钥的非对称加密,其中用户每次使用服务都需要输入密码。然后使用设备上的公钥加密密码并发送到服务器。在这里,即使入侵者知道了加密密码,他也无法对此做任何事情,因为他无法使用该公钥解密它,并且只需要服务器的私钥。

      无论如何,如果想要使用android的AccountManager系统并保持高水平的安全性,可以不在设备上保存任何令牌。然后可以像这样覆盖 AbstractAccountAuthenticator 中的 getAuthToken 方法:

      @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 存在。因此,只会返回 case 3,然后可以在相关回调中设置它以打开一个Activity,用户在其中输入用户名和密码进行身份验证。

      我不确定在此处进一步描述这一点是否走在正确的轨道上,但my website posts on AccountManager 可能会有所帮助,以防万一。

      【讨论】:

        猜你喜欢
        • 2019-11-02
        • 1970-01-01
        • 2018-12-06
        • 2011-09-12
        • 2011-10-30
        • 2017-01-06
        • 2018-04-18
        • 2016-12-24
        • 1970-01-01
        相关资源
        最近更新 更多