From a746e79b9117c058188b992a3519e5192f7e2113 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 15 May 2021 14:45:50 +0200 Subject: [PATCH] Auth: Update API implementation --- .../android/auth/IAuthManagerService.aidl | 5 +- .../gms/auth/AuthManagerServiceImpl.java | 168 +++++++++++------- 2 files changed, 107 insertions(+), 66 deletions(-) diff --git a/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl index 75a949b8..3bc93136 100644 --- a/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl +++ b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl @@ -9,6 +9,9 @@ import com.google.android.gms.auth.AccountChangeEventsRequest; interface IAuthManagerService { Bundle getToken(String accountName, String scope, in Bundle extras) = 0; Bundle clearToken(String token, in Bundle extras) = 1; - AccountChangeEventsResponse getChangeEvents(in AccountChangeEventsRequest request) = 2; + AccountChangeEventsResponse getChangeEvents(in AccountChangeEventsRequest request) = 2; Bundle getTokenWithAccount(in Account account, String scope, in Bundle extras) = 4; + Bundle getAccounts(in Bundle extras) = 5; + Bundle removeAccount(in Account account) = 6; + Bundle requestGoogleAccountsAccess(String packageName) = 7; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java index 0639fe03..b2722270 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java @@ -18,6 +18,8 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -43,7 +45,9 @@ import org.microg.gms.common.PackageUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; +import static android.accounts.AccountManager.KEY_ACCOUNTS; import static android.accounts.AccountManager.KEY_ACCOUNT_NAME; import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; import static android.accounts.AccountManager.KEY_AUTHTOKEN; @@ -53,6 +57,7 @@ import static org.microg.gms.auth.AskPermissionActivity.EXTRA_CONSENT_DATA; public class AuthManagerServiceImpl extends IAuthManagerService.Stub { private static final String TAG = "GmsAuthManagerSvc"; + public static final String KEY_ACCOUNT_FEATURES = "account_features"; public static final String KEY_AUTHORITY = "authority"; public static final String KEY_CALLBACK_INTENT = "callback_intent"; public static final String KEY_CALLER_UID = "callerUid"; @@ -74,68 +79,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { } @Override - public Bundle getToken(String accountName, String scope, Bundle extras) throws RemoteException { - String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME); - if (packageName == null || packageName.isEmpty()) - packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME); - packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); - boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); - - Log.d(TAG, "getToken: account:" + accountName + " scope:" + scope + " extras:" + extras + ", notify: " + notify); - - /* - * TODO: This scope seems to be invalid (according to https://developers.google.com/oauthplayground/), - * but is used in some applications anyway. Removing it is unlikely a good solution, but works for now. - */ - scope = scope.replace("https://www.googleapis.com/auth/identity.plus.page.impersonation ", ""); - - AuthManager authManager = new AuthManager(context, accountName, packageName, scope); - Bundle result = new Bundle(); - result.putString(KEY_ACCOUNT_NAME, accountName); - result.putString(KEY_ACCOUNT_TYPE, authManager.getAccountType()); - if (!authManager.accountExists()) { - result.putString(KEY_ERROR, "NetworkError"); - return result; - } - try { - AuthResponse res = authManager.requestAuth(false); - if (res.auth != null) { - Log.d(TAG, "getToken: " + res); - result.putString(KEY_AUTHTOKEN, res.auth); - Bundle details = new Bundle(); - details.putParcelable("TokenData", new TokenData(res.auth, res.expiry, scope.startsWith("oauth2:"), getScopes(scope))); - result.putBundle("tokenDetails", details); - result.putString(KEY_ERROR, "OK"); - } else { - result.putString(KEY_ERROR, "NeedPermission"); - Intent i = new Intent(context, AskPermissionActivity.class); - i.putExtras(extras); - i.putExtra(KEY_ANDROID_PACKAGE_NAME, packageName); - i.putExtra(KEY_ACCOUNT_TYPE, authManager.getAccountType()); - i.putExtra(KEY_ACCOUNT_NAME, accountName); - i.putExtra(KEY_AUTHTOKEN, scope); - try { - if (res.consentDataBase64 != null) - i.putExtra(EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.URL_SAFE)); - } catch (Exception e) { - Log.w(TAG, "Can't decode consent data: ", e); - } - if (notify) { - NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.notify(packageName.hashCode(), new NotificationCompat.Builder(context) - .setContentIntent(PendingIntent.getActivity(context, 0, i, 0)) - .setContentTitle(context.getString(R.string.auth_notification_title)) - .setContentText(context.getString(R.string.auth_notification_content, getPackageLabel(packageName, context.getPackageManager()))) - .setSmallIcon(android.R.drawable.stat_notify_error) - .build()); - } - result.putParcelable(KEY_USER_RECOVERY_INTENT, i); - } - } catch (IOException e) { - Log.w(TAG, e); - result.putString(KEY_ERROR, "NetworkError"); - } - return result; + public Bundle getToken(String accountName, String scope, Bundle extras) { + return getTokenWithAccount(new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE), scope, extras); } private List getScopes(String scope) { @@ -162,12 +107,105 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { } @Override - public Bundle getTokenWithAccount(Account account, String scope, Bundle extras) throws RemoteException { - return getToken(account.name, scope, extras); + public Bundle getTokenWithAccount(Account account, String scope, Bundle extras) { + String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME); + if (packageName == null || packageName.isEmpty()) + packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME); + packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); + boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); + + Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify); + + /* + * TODO: This scope seems to be invalid (according to https://developers.google.com/oauthplayground/), + * but is used in some applications anyway. Removing it is unlikely a good solution, but works for now. + */ + scope = scope.replace("https://www.googleapis.com/auth/identity.plus.page.impersonation ", ""); + + AuthManager authManager = new AuthManager(context, account.name, packageName, scope); + Bundle result = new Bundle(); + result.putString(KEY_ACCOUNT_NAME, account.name); + result.putString(KEY_ACCOUNT_TYPE, authManager.getAccountType()); + if (!authManager.accountExists()) { + result.putString(KEY_ERROR, "NetworkError"); + return result; + } + try { + AuthResponse res = authManager.requestAuth(false); + if (res.auth != null) { + Log.d(TAG, "getToken: " + res); + result.putString(KEY_AUTHTOKEN, res.auth); + Bundle details = new Bundle(); + details.putParcelable("TokenData", new TokenData(res.auth, res.expiry, scope.startsWith("oauth2:"), getScopes(scope))); + result.putBundle("tokenDetails", details); + result.putString(KEY_ERROR, "OK"); + } else { + result.putString(KEY_ERROR, "NeedPermission"); + Intent i = new Intent(context, AskPermissionActivity.class); + i.putExtras(extras); + i.putExtra(KEY_ANDROID_PACKAGE_NAME, packageName); + i.putExtra(KEY_ACCOUNT_TYPE, authManager.getAccountType()); + i.putExtra(KEY_ACCOUNT_NAME, account.name); + i.putExtra(KEY_AUTHTOKEN, scope); + try { + if (res.consentDataBase64 != null) + i.putExtra(EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.URL_SAFE)); + } catch (Exception e) { + Log.w(TAG, "Can't decode consent data: ", e); + } + if (notify) { + NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(packageName.hashCode(), new NotificationCompat.Builder(context) + .setContentIntent(PendingIntent.getActivity(context, 0, i, 0)) + .setContentTitle(context.getString(R.string.auth_notification_title)) + .setContentText(context.getString(R.string.auth_notification_content, getPackageLabel(packageName, context.getPackageManager()))) + .setSmallIcon(android.R.drawable.stat_notify_error) + .build()); + } + result.putParcelable(KEY_USER_RECOVERY_INTENT, i); + } + } catch (IOException e) { + Log.w(TAG, e); + result.putString(KEY_ERROR, "NetworkError"); + } + return result; } @Override - public Bundle clearToken(String token, Bundle extras) throws RemoteException { + public Bundle getAccounts(Bundle extras) { + PackageUtils.assertExtendedAccess(context); + String[] accountFeatures = extras.getStringArray(KEY_ACCOUNT_FEATURES); + String accountType = extras.getString(KEY_ACCOUNT_TYPE); + Account[] accounts; + if (accountFeatures != null) { + try { + accounts = AccountManager.get(context).getAccountsByTypeAndFeatures(accountType, accountFeatures, null, null).getResult(5, TimeUnit.SECONDS); + } catch (Exception e) { + Log.w(TAG, e); + return null; + } + } else { + accounts = AccountManager.get(context).getAccountsByType(accountType); + } + Bundle res = new Bundle(); + res.putParcelableArray(KEY_ACCOUNTS, accounts); + return res; + } + + @Override + public Bundle removeAccount(Account account) { + Log.w(TAG, "Not implemented: removeAccount(" + account + ")"); + return null; + } + + @Override + public Bundle requestGoogleAccountsAccess(String packageName) throws RemoteException { + Log.w(TAG, "Not implemented: requestGoogleAccountsAccess(" + packageName + ")"); + return null; + } + + @Override + public Bundle clearToken(String token, Bundle extras) { String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME); if (packageName == null) packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME); packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0));