package com.zegoggles.smssync.auth;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.zegoggles.smssync.preferences.AuthPreferences;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import static android.text.TextUtils.isEmpty;
import static com.zegoggles.smssync.App.TAG;
import static com.zegoggles.smssync.activity.auth.AccountManagerAuthActivity.AUTH_TOKEN_TYPE;
import static com.zegoggles.smssync.activity.auth.AccountManagerAuthActivity.GOOGLE_TYPE;
@TargetApi(5)
public class TokenRefresher {
private @Nullable final AccountManager accountManager;
private final OAuth2Client oauth2Client;
private AuthPreferences authPreferences;
public TokenRefresher(Context context, OAuth2Client oauth2Client, AuthPreferences authPreferences) {
this(Build.VERSION.SDK_INT >= 5 ? AccountManager.get(context) : null, oauth2Client, authPreferences);
}
TokenRefresher(@Nullable AccountManager accountManager, OAuth2Client oauth2Client,
AuthPreferences authPreferences) {
this.authPreferences = authPreferences;
this.oauth2Client = oauth2Client;
this.accountManager = accountManager;
}
public void refreshOAuth2Token() throws TokenRefreshException{
final String token = authPreferences.getOauth2Token();
final String refreshToken = authPreferences.getOauth2RefreshToken();
final String name = authPreferences.getUsername();
if (isEmpty(token)) {
throw new TokenRefreshException("no current token set");
}
if (!isEmpty(refreshToken)) {
// user authenticated using webflow
refreshUsingOAuth2Client(name, refreshToken);
} else {
refreshUsingAccountManager(token, name);
}
}
private void refreshUsingAccountManager(String token, String name) throws TokenRefreshException {
if (accountManager == null) throw new TokenRefreshException("account manager is null");
invalidateToken(token);
try {
Bundle bundle = accountManager.getAuthToken(
new Account(name, GOOGLE_TYPE),
AUTH_TOKEN_TYPE,
true, /* notify on failure */
null, /* callback */
null /* handler */
).getResult();
if (bundle != null) {
String newToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
if (!isEmpty(newToken)) {
authPreferences.setOauth2Token(name, newToken, null);
} else {
throw new TokenRefreshException("no new token obtained");
}
} else {
throw new TokenRefreshException("no bundle received from accountmanager");
}
} catch (OperationCanceledException e) {
Log.w(TAG, e);
throw new TokenRefreshException(e);
} catch (IOException e) {
Log.w(TAG, e);
throw new TokenRefreshException(e);
} catch (AuthenticatorException e) {
Log.w(TAG, e);
throw new TokenRefreshException(e);
}
}
public boolean invalidateToken(String token) {
if (accountManager != null) {
// USE_CREDENTIALS permission should be enough according to docs
// but some systems require MANAGE_ACCOUNTS
// java.lang.SecurityException: caller uid 10051 lacks android.permission.MANAGE_ACCOUNTS
try {
accountManager.invalidateAuthToken(GOOGLE_TYPE, token);
return true;
} catch (SecurityException e) {
Log.w(TAG, e);
}
}
return false;
}
private void refreshUsingOAuth2Client(String name, String refreshToken) throws TokenRefreshException {
try {
final OAuth2Token token = oauth2Client.refreshToken(refreshToken);
authPreferences.setOauth2Token(name, token.accessToken, isEmpty(token.refreshToken) ? refreshToken : token.refreshToken);
} catch (IOException e) {
throw new TokenRefreshException(e);
}
}
}