AccountManagerを使用してユーザーのアカウントを保存するアプリがあります。ユーザーは、OAuth2.0パスワード-ユーザー名資格情報フローを使用して、my REST APIを介してログインおよびサインアップします。
ユーザーが受け取るアクセストークンは2時間で期限切れになり、再び期限切れになるまで更新する必要があります。
このさわやかな機能をオーセンティケーターに実装する必要があります。
次のフィールドを持つAccessToken
というモデルがあります。
String accessToken, String tokenType, Long expiresIn, String refreshToken, String scope, Long createdAt
。
そのため、現在、getAuthToken
クラスのAccountAuthenticator
メソッドで、このAccessToken
オブジェクトを受け取り、そのaccessToken
フィールドをアカウントのAuth Token
として使用しています。マネージャー。
必要なのは、アカウントマネージャーを使用して更新トークンと認証トークンの両方を何らかの方法で保存し、アプリがAPIにアクセスしようとしてエラー応答を受け取った場合:{"error": "access token expired"}
、refreshToken
以前に受信したAccessToken
オブジェクトからの文字列。しかし、どうすればいいのかわかりません。
オーセンティケータークラスのgetAuthToken
メソッドは、現在次のようになっています。
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) &&
!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE, "Invalid authTokenType");
return result;
}
final AccountManager manager = AccountManager.get(context.getApplicationContext());
String authToken = manager.peekAuthToken(account, authTokenType);
Log.d("Discounty", TAG + " > peekAuthToken returned - " + authToken);
if (TextUtils.isEmpty(authToken)) {
final String password = manager.getPassword(account);
if (password != null) {
try {
authToken = discountyService.getAccessToken(DiscountyService.ACCESS_GRANT_TYPE,
account.name, password).toBlocking().first().getAccessToken();
// =======
// Here the above discountyService.getAccessToken(...) call returns
// AccessToken object on which I call the .getAccessToken()
// getter which returns a string.
// =======
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (!TextUtils.isEmpty(authToken)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
クラスAccountGeneral
にはいくつかの定数が含まれています。
public class AccountGeneral {
public static final String ACCOUNT_TYPE = "com.discounty";
public static final String ACCOUNT_NAME = "Discounty";
public static final String AUTHTOKEN_TYPE_READ_ONLY = "Read only";
public static final String AUTHTOKE_TYPE_READ_ONLY_LABEL = "Read only access to a Discounty account";
public static final String AUTHTOKEN_TYPE_FULL_ACCESS = "Full access";
public static final String AUTHTOKEN_TYPE_FULL_ACCESS_LABEL = "Full access to a Discounty account";
}
アプリもSyncAdapter
を使用し、APIと頻繁にやり取りしてサーバーとの間でデータを同期します。また、これらのAPI呼び出しでは、リクエストのパラメーターとしてアクセストークンを使用する必要があるため、実際にこの更新機能を実装して自動化する必要があります。
誰かがそれを正しく実装する方法を知っていますか?
PS:ローカルデータベースを使用してすべてのデータを保存し、トークンオブジェクトも保存できます。安全ではありませんが、これは簡単なハッキングのようです。たぶん、dbレコードとして一度に1つの更新トークンだけを保存し、アプリが新しいトークンを受け取ったときにそれを更新する必要がありますか?
PPS:APIの動作方法は自由に変更できるので、APIを改善してモバイルアプリを改善するための提案があれば、それも非常に高く評価されています。
以下を使用してアカウントを最初に追加するときに、更新トークンをアカウントのユーザーデータに保存できます。
Bundle userdata = new Bundle;
userdata.putString("refreshToken", refreshToken);
mAccountManager.addAccountExplicitly (account, password, userdata);
アカウントを追加した後、次のコマンドを呼び出してユーザーデータを設定することもできます。
mAccountManager.setUserData(account, "refreshToken", refreshToken);
アクセストークンの有効期限が切れたら、次のコマンドを呼び出して更新トークンを取得できます。
String refreshToken = mAccountManager.getUserData(account, "refreshToken");
RefreshTokenを使用して、新しいアクセストークンを取得します。