diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/LoginActivity.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/LoginActivity.kt index 7583769712..310d9a6dc6 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/LoginActivity.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/LoginActivity.kt @@ -885,7 +885,7 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted clientId = clientId, responseType = ResponseType.CODE.string, scope = scope, - prompt = if (!oidcSupported) "" else if (loginAction == ACTION_CREATE) "login" + prompt = if (!oidcSupported) "" else if (loginAction == ACTION_CREATE) OAuthUtils.getCreateAccountPrompt(scope) else mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_PROMPT, R.string.oauth2_openid_prompt), codeChallenge = authenticationViewModel.codeChallenge, state = authenticationViewModel.oidcState, diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/oauth/OAuthUtils.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/oauth/OAuthUtils.kt index 393a4ec204..db4d7f1814 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/oauth/OAuthUtils.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/authentication/oauth/OAuthUtils.kt @@ -96,6 +96,10 @@ class OAuthUtils { return "Basic " + Base64.encodeToString(credentials.toByteArray(), Base64.NO_WRAP) } + fun getCreateAccountPrompt(scope: String): String = + // Request offline consent without forcing fresh authentication. + if ("offline_access" in scope.split(' ')) "consent" else "login" + fun buildAuthorizationRequest( authorizationEndpoint: Uri, redirectUri: String, diff --git a/opencloudApp/src/test/java/eu/opencloud/android/presentation/authentication/oauth/OAuthPromptTest.kt b/opencloudApp/src/test/java/eu/opencloud/android/presentation/authentication/oauth/OAuthPromptTest.kt new file mode 100644 index 0000000000..9fbf2d7866 --- /dev/null +++ b/opencloudApp/src/test/java/eu/opencloud/android/presentation/authentication/oauth/OAuthPromptTest.kt @@ -0,0 +1,53 @@ +package eu.opencloud.android.presentation.authentication.oauth + +import android.net.Uri +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE, sdk = [28]) +class OAuthPromptTest { + @Test + fun offlineAccessRequiresConsentOnly() { + assertEquals("consent", OAuthUtils.getCreateAccountPrompt("openid profile email groups offline_access")) + assertEquals("consent", OAuthUtils.getCreateAccountPrompt(" offline_access openid ")) + } + + @Test + fun otherScopesKeepLogin() { + for (scope in listOf("", "openid profile email", "openid not_offline_access", "offline_access_extra")) { + assertEquals("login", OAuthUtils.getCreateAccountPrompt(scope)) + } + } + + @Test + fun authorizationRequestEncodesOnePromptAndPreservesPkceAndRedirect() { + val scope = "openid profile email groups offline_access" + val uri = OAuthUtils.buildAuthorizationRequest( + authorizationEndpoint = Uri.parse("https://idp.example/authorize?existing=value"), + redirectUri = "oc://android.opencloud.eu", + clientId = "OpenCloudAndroid", + responseType = "code", + scope = scope, + prompt = OAuthUtils.getCreateAccountPrompt(scope), + codeChallenge = "test-challenge", + state = "test-state", + username = null, + sendLoginHintAndUser = false, + ) + assertEquals(listOf("consent"), uri.getQueryParameters("prompt")) + assertTrue(uri.encodedQuery!!.contains("prompt=consent")) + assertEquals("oc://android.opencloud.eu", uri.getQueryParameter("redirect_uri")) + assertEquals("OpenCloudAndroid", uri.getQueryParameter("client_id")) + assertEquals(scope, uri.getQueryParameter("scope")) + assertEquals("S256", uri.getQueryParameter("code_challenge_method")) + assertEquals("test-challenge", uri.getQueryParameter("code_challenge")) + assertEquals("test-state", uri.getQueryParameter("state")) + assertEquals("value", uri.getQueryParameter("existing")) + println("Authorization request under test: $uri") + } +}