简体   繁体   English

Android OAuth2 Bearer令牌最佳做法

[英]Android OAuth2 Bearer token best practices

This nice tutorial is a very good introduction to account authentication on Android and doing it by utilizing Android's AccountManager . 这个很好的教程非常好地介绍了Android上的帐户身份验证,并通过使用Android的AccountManager

However, I need to create a client app for an OAuth2 API using a Bearer token for authentication. 但是,我需要使用承载令牌为OAuth2 API创建客户端应用程序以进行身份​​验证。 At the time of obtaining the token, I receive the expiry timestamp for it, but I am unclear about where to store and how to make use of it properly. 在获得令牌时,我收到了它的到期时间戳,但我不清楚存储的位置以及如何正确使用它。 Problem is, if I don't want to have unnecessary trips to the server, the app would realize that the Bearer had become invalid only after it receives a HTTP 401 error from the server when requesting any random resource. 问题是,如果我不想不必要地去服务器,应用程序会意识到,只有在请求任何随机资源时从服务器收到HTTP 401错误后,承载才会变为无效。 So, what is the best practice to tackle this: 那么,解决这个问题的最佳做法是什么:

  1. Should every network request in my code have a retry mechanism in case the bearer token has become invalid in meantime? 我的代码中的每个网络请求都应该具有重试机制,以防承载令牌在此期间变为无效吗? I would probably invalidateAuthToken when catching the exception and retry. 在捕获异常时我可能invalidateAuthToken并重试。
  2. Can Sync Adapter somehow help here? 同步适配器能以某种方式帮助吗?

As I am new to Android development, I expect that the solution may also be something completely different than I expect. 由于我是Android开发的新手,我希望解决方案也可能与我预期的完全不同。

If it is relevant, I intend to use Volley for the server communication. 如果它是相关的,我打算使用Volley进行服务器通信。

I found out my own answers after a bit of investigation: 经过一番调查后我发现了自己的答案:

  1. Yes, calling AccountManager#invalidateAuthToken removes the last saved authentication token (access token in the OAuth2 case) and expects that you are detecting that on the next AccountAuthenticator#getAuthToken call. 是的,调用AccountManager#invalidateAuthToken将删除上次保存的身份验证令牌(OAuth2案例中的访问令牌),并期望您在下一个AccountAuthenticator#getAuthToken调用中检测到该AccountAuthenticator#getAuthToken For example, the following is my code for that method: 例如,以下是该方法的代码:

     @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { // Extract the username and password from the Account Manager, and ask // for an appropriate AuthToken. final AccountManager am = AccountManager.get(mContext); String authToken = am.peekAuthToken(account, authTokenType); // EXTRA: I am also storing the OAuth2 refresh token in the AccountManager Map<String, String> refreshResult = null; String refreshToken = am.getUserData(account, KEY_REFRESH_TOKEN); if (TextUtils.isEmpty(authToken) && !TextUtils.isEmpty(refreshToken)) { // lets try to refresh the token // EXTRA: AuthenticationProvider is my class for accessing the authentication server, getting new access and refresh token based on the existing refresh token refreshResult = AuthenticationProvider. refreshAccessToken(am.getUserData(account, KEY_REFRESH_TOKEN)); } // If we get a result from the refresh - we return it if (!refreshResult.isEmpty()) { authToken = refreshResult.get(AccountManager.KEY_AUTHTOKEN); // EXTRA: new refresh token used only in OAuth2 refreshToken = refreshResult.get(KEY_REFRESH_TOKEN); final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); // store the new tokens in the system am.setAuthToken(account, authTokenType, authToken); am.setUserData(account, KEY_REFRESH_TOKEN, refreshToken); result.putString(AccountManager.KEY_AUTHTOKEN, refreshResult.get(AccountManager.KEY_AUTHTOKEN)); result.putString(KEY_REFRESH_TOKEN, refreshResult.get(KEY_REFRESH_TOKEN)); return result; } // If we get here, then we couldn't access the user's password - so we // need to re-prompt them for their credentials. We do that by creating // an intent to display our AuthenticatorActivity. final Intent intent = new Intent(mContext, LoginActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; } 

    I also received a confirmation from the author of the blog post mentioned in the question. 我还收到了问题中提到的博客文章的作者的确认

  2. SyncAdapter s cannot help directly, as their true purpose is obtaining data from network asynchronously (for the developer) and transparently (for the user). SyncAdapter无法直接帮助,因为它们的真正目的是异步(对于开发人员)和透明地(对于用户)从网络获取数据。 They just use AbstractAccountAuthenticator and call its methods where appropriate. 他们只使用AbstractAccountAuthenticator并在适当的地方调用其方法。

I was going through the same problem before and make this library to handel OAuth2 in Android. 之前我遇到了同样的问题,并将此库设置为Android中的handel OAuth2。 but the library is an extension for Retrofit that simplifies the process of authenticating against an OAuth 2 provider but still you can use it. 但该库是Retrofit的扩展,它简化了对OAuth 2提供程序进行身份验证的过程,但您仍然可以使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM