Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9de165d6 authored by Fynn Godau's avatar Fynn Godau Committed by Jonathan Klee
Browse files

workaccount: Eliminate call to `AccountManager.addAccount`

Intune now sets a policy such that users are not allowed to create work
profiles anymore. This is good in principle because users are never
supposed to do this manually (and are, in fact, incapable of doing so).
Our code is also affected because `WorkAccountService` triggered sign-in
flow in `WorkAccountAuthenticator` through `AccountManager.addAccount`,
which is the action prohibited by the policy. We can still (and need to)
call `AccountManager.addAccountExplicitly` to register the account with
the system.
parent 7dc03f96
Loading
Loading
Loading
Loading
+54 −86
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2024 e foundation
 * SPDX-FileCopyrightText: 2026 e foundation
 * SPDX-License-Identifier: Apache-2.0
 */

@@ -40,72 +40,40 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica
        options: Bundle
    ): Bundle? {

        if (!WorkProfileSettings(context).allowCreateWorkAccount) {
            return Bundle().apply {
                putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION)
                putString(AccountManager.KEY_ERROR_MESSAGE, context.getString(R.string.auth_work_authenticator_disabled_error)
                )
            }
        } else if (
            !options.containsKey(KEY_ACCOUNT_CREATION_TOKEN)
            || options.getString(KEY_ACCOUNT_CREATION_TOKEN) == null
            || options.getInt(AccountManager.KEY_CALLER_UID) != android.os.Process.myUid()) {
            Log.e(TAG,
                "refusing to add account without creation token or from external app: " +
                        "could have been manually initiated by user (not supported) " +
                        "or by unauthorized app (not allowed)"
            )

            // TODO: The error message is not automatically displayed by the settings app as of now.
            // We can consider showing the error message through a popup instead.

        return Bundle().apply {
            putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION)
                putString(AccountManager.KEY_ERROR_MESSAGE, context.getString(R.string.auth_work_authenticator_add_manual_error)
            putString(
                AccountManager.KEY_ERROR_MESSAGE,
                context.getString(R.string.auth_work_authenticator_add_manual_error)
            )
        }
    }

        val oauthToken: String = options.getString(KEY_ACCOUNT_CREATION_TOKEN)!!
    fun addAccountInternal(
        accountCreationToken: String
    ): Account? {

        try {
            tryAddAccount(oauthToken, response)
        } catch (exception: Exception) {
            response.onResult(Bundle().apply {
                putInt(
                    AccountManager.KEY_ERROR_CODE,
                    AccountManager.ERROR_CODE_NETWORK_ERROR
                )
                putString(AccountManager.KEY_ERROR_MESSAGE, exception.message)
            })
        }

        /* Note: as is not documented, `null` must only be returned after `response.onResult` was
         * already called, hence forcing the requests to be synchronous. They are still async to
         * the caller's main thread because AccountManager forces potentially blocking operations,
         * like waiting for a response upon `addAccount`, not to be on the main thread.
         */
        if (!WorkProfileSettings(context).allowCreateWorkAccount) {
            // TODO: communicate error to user (use `R.string.auth_work_authenticator_disabled_error)`)
            Log.w(TAG, "creating a work account is disabled in microG settings")
            return null
        }

    @Throws(Exception::class)
    private fun tryAddAccount(
        oauthToken: String,
        response: AccountAuthenticatorResponse
    ) {
        try {
            val authResponse = AuthRequest().fromContext(context)
                .appIsGms()
                .callerIsGms()
                .service("ac2dm")
            .token(oauthToken).isAccessToken()
                .token(accountCreationToken).isAccessToken()
                .addAccount()
                .getAccountId()
                .droidguardResults(null)
                .response

            val accountManager = AccountManager.get(context)
            val account = Account(authResponse.email, AuthConstants.WORK_ACCOUNT_TYPE)
            if (accountManager.addAccountExplicitly(
                Account(authResponse.email, AuthConstants.WORK_ACCOUNT_TYPE),
                    account,
                    authResponse.token, Bundle().apply {
                        // Work accounts have no SID / LSID ("BAD_COOKIE") and no first/last name.
                        if (authResponse.accountId.isNotBlank()) {
@@ -129,10 +97,11 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica
                )

                // Report successful creation to caller
            response.onResult(Bundle().apply {
                putString(AccountManager.KEY_ACCOUNT_NAME, authResponse.email)
                putString(AccountManager.KEY_ACCOUNT_TYPE, AuthConstants.WORK_ACCOUNT_TYPE)
            })
                return account
            } else return null
        } catch (exception: Exception) {
            Log.w(TAG, exception)
            return null
        }
    }

@@ -234,7 +203,6 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica

        const val WORK_ACCOUNT_CHANGED_BOARDCAST = "org.microg.vending.WORK_ACCOUNT_CHANGED"

        const val KEY_ACCOUNT_CREATION_TOKEN = "creationToken"
        private const val KEY_GOOGLE_USER_ID = AuthConstants.GOOGLE_USER_ID
    }
}
 No newline at end of file
+5 −25
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2024 e foundation
 * SPDX-FileCopyrightText: 2026 e foundation
 * SPDX-License-Identifier: Apache-2.0
 */

@@ -13,12 +13,11 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Parcel
import android.util.Log
import com.google.android.gms.auth.account.IWorkAccountCallback
import com.google.android.gms.auth.account.IWorkAccountService
import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator.Companion.KEY_ACCOUNT_CREATION_TOKEN
import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator
import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator.Companion.WORK_ACCOUNT_CHANGED_BOARDCAST
import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticatorService
import com.google.android.gms.common.Feature
@@ -27,7 +26,6 @@ import com.google.android.gms.common.internal.ConnectionInfo
import com.google.android.gms.common.internal.GetServiceRequest
import com.google.android.gms.common.internal.IGmsCallbacks
import org.microg.gms.BaseService
import org.microg.gms.auth.AuthConstants
import org.microg.gms.common.GmsService
import org.microg.gms.common.PackageUtils

@@ -97,30 +95,12 @@ class WorkAccountServiceImpl(val context: Context) : IWorkAccountService.Stub()

    override fun addWorkAccount(
        callback: IWorkAccountCallback?,
        token: String?
        token: String
    ) {
        Log.d(TAG, "addWorkAccount with token $token")
        val future = accountManager.addAccount(
            AuthConstants.WORK_ACCOUNT_TYPE,
            null,
            null,
            Bundle().apply { putString(KEY_ACCOUNT_CREATION_TOKEN, token) },
            null,
            null,
            null
        )
        Thread {
            try {
                future.result.let { result ->
                    callback?.onAccountAdded(
                        Account(
                            result.getString(AccountManager.KEY_ACCOUNT_NAME)!!,
                            result.getString(AccountManager.KEY_ACCOUNT_TYPE)!!
                        )
                    )
                }
            } catch (e: Exception) {
                Log.e(TAG, "could not add work account with error message: ${e.message}")
            WorkAccountAuthenticator(context).addAccountInternal(token)?.let {
                callback?.onAccountAdded(it)
            }
        }.start()
    }