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

Verified Commit b1a9de53 authored by Romain Hunault's avatar Romain Hunault 💻
Browse files

fix(auth): validate Murena offline access auth state

parent 3c40a286
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
package at.bitfire.davdroid.authorization

import net.openid.appauth.AuthState
import net.openid.appauth.AuthorizationException

object MurenaOfflineAccessValidator {

    fun hasUsableOfflineAccess(authState: AuthState): Boolean {
        return hasUsableOfflineAccess(
            accessToken = authState.accessToken,
            refreshToken = authState.refreshToken,
            authorizationException = authState.authorizationException
        )
    }

    internal fun hasUsableOfflineAccess(
        accessToken: String?,
        refreshToken: String?,
        authorizationException: AuthorizationException?
    ): Boolean {
        return authorizationException == null &&
            !accessToken.isNullOrBlank() &&
            !refreshToken.isNullOrBlank()
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.os.Bundle
import android.view.View
import at.bitfire.davdroid.ECloudAccountHelper
import at.bitfire.davdroid.authorization.IdentityProvider
import at.bitfire.davdroid.authorization.MurenaOfflineAccessValidator
import at.bitfire.davdroid.murenasso.MurenaSsoMigrationPreferences
import at.bitfire.davdroid.ui.account.SettingsActivity
import org.json.JSONObject
@@ -56,6 +57,11 @@ class MurenaOpenIdAuthFragment : OpenIdAuthenticationBaseFragment(IdentityProvid
            return
        }

        if (!MurenaOfflineAccessValidator.hasUsableOfflineAccess(getAuthState())) {
            handleLoginFailedToast()
            return
        }

        val userNameKey = "username"

        if (!userData.has(userNameKey)) {
+3 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.openid.appauth.AuthState.AuthStateAction
import net.openid.appauth.AuthState
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationResponse
import net.openid.appauth.AuthorizationService.TokenResponseCallback
@@ -203,6 +204,8 @@ abstract class OpenIdAuthenticationBaseFragment(private val identityProvider: Id
        finishActivity()
    }

    protected fun getAuthState(): AuthState = viewModel.getAuthState()

    protected fun proceedNext(userName: String, baseUrl: String, cardDavUrl: String? = null) {
        activity?.intent?.putExtra(LoginActivity.OPENID_AUTH_FLOW_COMPLETE, true)

+36 −0
Original line number Diff line number Diff line
package at.bitfire.davdroid.authorization

import net.openid.appauth.AuthorizationException
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class IdentityProviderTest {
@@ -11,4 +14,37 @@ class IdentityProviderTest {

        assertEquals("openid profile email", scope)
    }

    @Test
    fun `murena offline access validator accepts usable auth state data`() {
        assertTrue(
            MurenaOfflineAccessValidator.hasUsableOfflineAccess(
                accessToken = "access-token",
                refreshToken = "refresh-token",
                authorizationException = null
            )
        )
    }

    @Test
    fun `murena offline access validator rejects missing refresh token`() {
        assertFalse(
            MurenaOfflineAccessValidator.hasUsableOfflineAccess(
                accessToken = "access-token",
                refreshToken = null,
                authorizationException = null
            )
        )
    }

    @Test
    fun `murena offline access validator rejects authorization exception`() {
        assertFalse(
            MurenaOfflineAccessValidator.hasUsableOfflineAccess(
                accessToken = "access-token",
                refreshToken = "refresh-token",
                authorizationException = AuthorizationException.TokenRequestErrors.INVALID_GRANT
            )
        )
    }
}