Loading app/src/main/kotlin/at/bitfire/davdroid/network/OAuthInterceptor.kt +7 −1 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ import at.bitfire.davdroid.BuildConfig import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.runBlocking import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationService Loading Loading @@ -69,7 +71,11 @@ class OAuthInterceptor @AssistedInject constructor( // if possible, use cached access token val authState = readAuthState() ?: return null if (authState.isAuthorized && authState.accessToken != null && !authState.needsTokenRefresh) { val isValidAccessToken = runBlocking { !authState.needsTokenRefresh || AccountHelper.isValidAccessToken(authState) } if (authState.isAuthorized && authState.accessToken != null && isValidAccessToken) { if (BuildConfig.DEBUG) // log sensitive information (refresh/access token) only in debug builds logger.log(Level.FINEST, "Using cached AuthState", authState.jsonSerializeString()) return authState.accessToken Loading app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +2 −4 Original line number Diff line number Diff line Loading @@ -136,11 +136,9 @@ class AccountSettings @AssistedInject constructor( } fun updateAuthState(authState: AuthState) { OAuthMurena.newAuthState(authState)?.let { authState -> accountManager.setAndVerifyUserData(account, KEY_AUTH_STATE, authState.jsonSerializeString()) OAuthMurena.saveAuthState(context, account, authState) } } /** * Returns whether users can modify credentials from the account settings screen. Loading app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt +1 −9 Original line number Diff line number Diff line Loading @@ -26,9 +26,9 @@ import androidx.core.net.toUri import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.settings.Credentials import at.bitfire.davdroid.sync.account.setAndVerifyUserData import foundation.e.accountmanager.pref.AuthStatePrefUtils import com.owncloud.android.lib.common.accounts.AccountUtils import foundation.e.accountmanager.AccountTypes import foundation.e.accountmanager.pref.AuthStatePrefUtils import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationServiceConfiguration Loading Loading @@ -108,12 +108,4 @@ object OAuthMurena { logger.log(Level.INFO, "Saved new authState $stateJson") } } fun newAuthState(authState: AuthState?): AuthState? = authState?.apply { needsTokenRefresh = scopeSet?.contains("offline_access") != true if (BuildConfig.DEBUG) { logger.log(Level.INFO, "needsTokenRefresh: $needsTokenRefresh") } } } app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt +30 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,13 @@ import at.bitfire.davdroid.settings.AccountSettings import foundation.e.accountmanager.AccountTypes import foundation.e.accountmanager.sync.SyncBroadcastReceiver import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import net.openid.appauth.AuthState import java.net.HttpURLConnection import java.net.URL import java.util.logging.Level import java.util.logging.Logger object AccountHelper { private const val MAIL_PACKAGE = "foundation.e.mail" Loading @@ -38,6 +45,8 @@ object AccountHelper { private const val DRIVE_ACTION_ADD_ACCOUNT = "$DRIVE_PACKAGE_NAME.action.ADD_ACCOUNT" private const val DRIVE_RECEIVER_CLASS = "$DRIVE_PACKAGE_NAME.account.receivers.AccountAddedReceiver" val logger: Logger = Logger.getLogger(this.javaClass.name) fun getAllAccounts(accountManager: AccountManager): Array<Account> { val allAccounts = mutableListOf<Account>() for (type in AccountTypes.getAccountTypes()) { Loading @@ -54,6 +63,27 @@ object AccountHelper { return allAccounts.toTypedArray() } suspend fun isValidAccessToken(authState: AuthState): Boolean = withContext(Dispatchers.IO) { try { val endpoint = authState.authorizationServiceConfiguration ?.discoveryDoc ?.userinfoEndpoint ?.toString() ?: return@withContext false logger.fine("Checking current access token") (URL(endpoint).openConnection() as HttpURLConnection).run { setRequestProperty("Authorization", "Bearer ${authState.accessToken}") instanceFollowRedirects = false val valid = responseCode == HttpURLConnection.HTTP_OK disconnect() valid } } catch (ex: Exception) { logger.log(Level.SEVERE, "Failed to access userInfo endpoint", ex) false } } fun alreadyHasAccount(context: Context): Boolean { val accountManager = AccountManager.get(context) val accounts = accountManager.getAccountsByType(AccountTypes.Murena.accountType) Loading Loading
app/src/main/kotlin/at/bitfire/davdroid/network/OAuthInterceptor.kt +7 −1 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ import at.bitfire.davdroid.BuildConfig import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.runBlocking import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationService Loading Loading @@ -69,7 +71,11 @@ class OAuthInterceptor @AssistedInject constructor( // if possible, use cached access token val authState = readAuthState() ?: return null if (authState.isAuthorized && authState.accessToken != null && !authState.needsTokenRefresh) { val isValidAccessToken = runBlocking { !authState.needsTokenRefresh || AccountHelper.isValidAccessToken(authState) } if (authState.isAuthorized && authState.accessToken != null && isValidAccessToken) { if (BuildConfig.DEBUG) // log sensitive information (refresh/access token) only in debug builds logger.log(Level.FINEST, "Using cached AuthState", authState.jsonSerializeString()) return authState.accessToken Loading
app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +2 −4 Original line number Diff line number Diff line Loading @@ -136,11 +136,9 @@ class AccountSettings @AssistedInject constructor( } fun updateAuthState(authState: AuthState) { OAuthMurena.newAuthState(authState)?.let { authState -> accountManager.setAndVerifyUserData(account, KEY_AUTH_STATE, authState.jsonSerializeString()) OAuthMurena.saveAuthState(context, account, authState) } } /** * Returns whether users can modify credentials from the account settings screen. Loading
app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt +1 −9 Original line number Diff line number Diff line Loading @@ -26,9 +26,9 @@ import androidx.core.net.toUri import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.settings.Credentials import at.bitfire.davdroid.sync.account.setAndVerifyUserData import foundation.e.accountmanager.pref.AuthStatePrefUtils import com.owncloud.android.lib.common.accounts.AccountUtils import foundation.e.accountmanager.AccountTypes import foundation.e.accountmanager.pref.AuthStatePrefUtils import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationServiceConfiguration Loading Loading @@ -108,12 +108,4 @@ object OAuthMurena { logger.log(Level.INFO, "Saved new authState $stateJson") } } fun newAuthState(authState: AuthState?): AuthState? = authState?.apply { needsTokenRefresh = scopeSet?.contains("offline_access") != true if (BuildConfig.DEBUG) { logger.log(Level.INFO, "needsTokenRefresh: $needsTokenRefresh") } } }
app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt +30 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,13 @@ import at.bitfire.davdroid.settings.AccountSettings import foundation.e.accountmanager.AccountTypes import foundation.e.accountmanager.sync.SyncBroadcastReceiver import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import net.openid.appauth.AuthState import java.net.HttpURLConnection import java.net.URL import java.util.logging.Level import java.util.logging.Logger object AccountHelper { private const val MAIL_PACKAGE = "foundation.e.mail" Loading @@ -38,6 +45,8 @@ object AccountHelper { private const val DRIVE_ACTION_ADD_ACCOUNT = "$DRIVE_PACKAGE_NAME.action.ADD_ACCOUNT" private const val DRIVE_RECEIVER_CLASS = "$DRIVE_PACKAGE_NAME.account.receivers.AccountAddedReceiver" val logger: Logger = Logger.getLogger(this.javaClass.name) fun getAllAccounts(accountManager: AccountManager): Array<Account> { val allAccounts = mutableListOf<Account>() for (type in AccountTypes.getAccountTypes()) { Loading @@ -54,6 +63,27 @@ object AccountHelper { return allAccounts.toTypedArray() } suspend fun isValidAccessToken(authState: AuthState): Boolean = withContext(Dispatchers.IO) { try { val endpoint = authState.authorizationServiceConfiguration ?.discoveryDoc ?.userinfoEndpoint ?.toString() ?: return@withContext false logger.fine("Checking current access token") (URL(endpoint).openConnection() as HttpURLConnection).run { setRequestProperty("Authorization", "Bearer ${authState.accessToken}") instanceFollowRedirects = false val valid = responseCode == HttpURLConnection.HTTP_OK disconnect() valid } } catch (ex: Exception) { logger.log(Level.SEVERE, "Failed to access userInfo endpoint", ex) false } } fun alreadyHasAccount(context: Context): Boolean { val accountManager = AccountManager.get(context) val accounts = accountManager.getAccountsByType(AccountTypes.Murena.accountType) Loading