diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b5581c340..c5ca19ae1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -37,6 +37,12 @@
+
+
+
+
+
+
{
- if (viewModel.isLogistrationEnabled) {
+ val authCode = authCode;
+ if (viewModel.isLogistrationEnabled && authCode == null) {
addFragment(LogistrationFragment())
} else {
- addFragment(SignInFragment())
+ val bundle = Bundle()
+ bundle.putString("auth_code", authCode)
+ val fragment = SignInFragment()
+ fragment.arguments = bundle
+ addFragment(fragment)
}
}
diff --git a/app/src/main/java/org/openedx/app/di/AppModule.kt b/app/src/main/java/org/openedx/app/di/AppModule.kt
index 860e98de0..60d8a52ff 100644
--- a/app/src/main/java/org/openedx/app/di/AppModule.kt
+++ b/app/src/main/java/org/openedx/app/di/AppModule.kt
@@ -20,6 +20,7 @@ import org.openedx.app.room.DATABASE_NAME
import org.openedx.app.system.notifier.AppNotifier
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.AuthRouter
+import org.openedx.auth.presentation.sso.BrowserAuthHelper
import org.openedx.auth.presentation.sso.FacebookAuthHelper
import org.openedx.auth.presentation.sso.GoogleAuthHelper
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
@@ -153,4 +154,5 @@ val appModule = module {
factory { FacebookAuthHelper() }
factory { GoogleAuthHelper(get()) }
factory { MicrosoftAuthHelper() }
+ factory { BrowserAuthHelper(get()) }
}
diff --git a/app/src/main/java/org/openedx/app/di/ScreenModule.kt b/app/src/main/java/org/openedx/app/di/ScreenModule.kt
index 96fc024d0..31115ebf5 100644
--- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt
+++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt
@@ -74,6 +74,7 @@ val screenModule = module {
get(),
get(),
get(),
+ get(),
courseId,
)
}
diff --git a/auth/build.gradle b/auth/build.gradle
index 02b94a587..d2ab1cc0c 100644
--- a/auth/build.gradle
+++ b/auth/build.gradle
@@ -55,6 +55,7 @@ android {
dependencies {
implementation project(path: ':core')
+ implementation 'androidx.browser:browser:1.7.0'
implementation "androidx.credentials:credentials:1.2.0"
implementation "androidx.credentials:credentials-play-services-auth:1.2.0"
implementation "com.facebook.android:facebook-login:16.2.0"
diff --git a/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt b/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
index 903cbd62e..6d40554ea 100644
--- a/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
+++ b/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
@@ -32,6 +32,14 @@ interface AuthApi {
@Field("asymmetric_jwt") isAsymmetricJwt: Boolean = true,
): AuthResponse
+ @FormUrlEncoded
+ @POST(ApiConstants.URL_ACCESS_TOKEN)
+ suspend fun getAccessTokenFromCode(
+ @Field("grant_type") grantType: String,
+ @Field("client_id") clientId: String,
+ @Field("code") code: String,
+ ): AuthResponse
+
@FormUrlEncoded
@POST(ApiConstants.URL_ACCESS_TOKEN)
fun refreshAccessToken(
diff --git a/auth/src/main/java/org/openedx/auth/data/model/AuthType.kt b/auth/src/main/java/org/openedx/auth/data/model/AuthType.kt
index 5addd621c..c56ba0cf1 100644
--- a/auth/src/main/java/org/openedx/auth/data/model/AuthType.kt
+++ b/auth/src/main/java/org/openedx/auth/data/model/AuthType.kt
@@ -13,4 +13,5 @@ enum class AuthType(val postfix: String, val methodName: String) {
GOOGLE(ApiConstants.AUTH_TYPE_GOOGLE, "Google"),
FACEBOOK(ApiConstants.AUTH_TYPE_FB, "Facebook"),
MICROSOFT(ApiConstants.AUTH_TYPE_MICROSOFT, "Microsoft"),
+ BROWSER(ApiConstants.AUTH_TYPE_BROWSER, "Browser")
}
diff --git a/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt b/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
index 6cf54a7f1..5c7e464cb 100644
--- a/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
+++ b/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
@@ -1,5 +1,6 @@
package org.openedx.auth.data.repository
+import android.util.Log
import org.openedx.auth.data.api.AuthApi
import org.openedx.auth.data.model.AuthType
import org.openedx.auth.data.model.ValidationFields
@@ -43,6 +44,14 @@ class AuthRepository(
.processAuthResponse()
}
+ suspend fun browserAuthCodeLogin(code: String) {
+ api.getAccessTokenFromCode(
+ grantType = ApiConstants.GRANT_TYPE_CODE,
+ clientId = config.getOAuthClientId(),
+ code = code,
+ ).mapToDomain().processAuthResponse()
+ }
+
suspend fun getRegistrationFields(): List {
return api.getRegistrationFields().fields?.map { it.mapToDomain() } ?: emptyList()
}
@@ -60,12 +69,16 @@ class AuthRepository(
}
private suspend fun AuthResponse.processAuthResponse() {
+ Log.d("MMMMMMMM", error.toString())
if (error != null) {
throw EdxError.UnknownException(error!!)
}
preferencesManager.accessToken = accessToken ?: ""
preferencesManager.refreshToken = refreshToken ?: ""
preferencesManager.accessTokenExpiresAt = getTokenExpiryTime()
+ Log.d("MMMMMMMM", preferencesManager.accessToken)
+ Log.d("MMMMMMMM", preferencesManager.refreshToken)
+ Log.d("MMMMMMMM", preferencesManager.accessTokenExpiresAt.toString())
val user = api.getProfile()
preferencesManager.user = user
}
diff --git a/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt b/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
index 00fe509af..d81c51eaf 100644
--- a/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
+++ b/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
@@ -18,6 +18,10 @@ class AuthInteractor(private val repository: AuthRepository) {
repository.socialLogin(token, authType)
}
+ suspend fun loginAuthCode(authCode: String) {
+ repository.browserAuthCodeLogin(authCode)
+ }
+
suspend fun getRegistrationFields(): List {
return repository.getRegistrationFields()
}
diff --git a/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt
index 5c77688c3..a53e04ac5 100644
--- a/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt
@@ -1,6 +1,8 @@
package org.openedx.auth.presentation.logistration
+import android.content.Intent
import android.content.res.Configuration
+import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
@@ -41,6 +43,8 @@ import androidx.fragment.app.Fragment
import org.koin.android.ext.android.inject
import org.openedx.auth.R
import org.openedx.auth.presentation.AuthRouter
+import org.openedx.core.config.Config
+import org.openedx.core.presentation.dialog.alert.ActionDialogFragment
import org.openedx.core.ui.AuthButtonsPanel
import org.openedx.core.ui.SearchBar
import org.openedx.core.ui.displayCutoutForLandscape
@@ -48,11 +52,13 @@ import org.openedx.core.ui.noRippleClickable
import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.UrlUtils
import org.openedx.core.R as coreR
class LogistrationFragment : Fragment() {
private val router: AuthRouter by inject()
+ private val config: Config by inject()
override fun onCreateView(
inflater: LayoutInflater,
@@ -68,7 +74,15 @@ class LogistrationFragment : Fragment() {
router.navigateToSignIn(parentFragmentManager, courseId)
},
onRegisterClick = {
- router.navigateToSignUp(parentFragmentManager, courseId)
+ if (config.isBrowserRegistrationEnabled()) {
+ UrlUtils.openInBrowser(
+ activity = context,
+ apiHostUrl = config.getApiHostURL(),
+ url = "/register",
+ )
+ } else {
+ router.navigateToSignUp(parentFragmentManager, courseId)
+ }
},
onSearchClick = { querySearch ->
router.navigateToDiscoverCourses(parentFragmentManager, querySearch)
diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
index 12eeedc7d..84fff3146 100644
--- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
@@ -1,6 +1,7 @@
package org.openedx.auth.presentation.signin
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.runtime.LaunchedEffect
@@ -44,6 +45,11 @@ class SignInFragment : Fragment() {
val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState(null)
if (appUpgradeEvent == null) {
+ val authCode = arguments?.getString("auth_code")
+ if (authCode is String && !state.loginFailure && !state.loginSuccess) {
+ arguments?.remove("auth_code")
+ viewModel.signInAuthCode(authCode)
+ }
LoginScreen(
windowSize = windowSize,
state = state,
@@ -65,6 +71,10 @@ class SignInFragment : Fragment() {
router.navigateToRestorePassword(parentFragmentManager)
}
+ AuthEvent.SignInBrowser -> {
+ viewModel.signInBrowser(requireActivity())
+ }
+
AuthEvent.RegisterClick -> {
viewModel.signUpClickedEvent()
router.navigateToSignUp(parentFragmentManager, null)
@@ -117,7 +127,9 @@ internal sealed interface AuthEvent {
object SignInGoogle : AuthEvent
object SignInFacebook : AuthEvent
object SignInMicrosoft : AuthEvent
+ object SignInBrowser : AuthEvent
object RegisterClick : AuthEvent
object ForgotPasswordClick : AuthEvent
object BackClick : AuthEvent
+
}
diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInUIState.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInUIState.kt
index 829d376f1..7231c9ed6 100644
--- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInUIState.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInUIState.kt
@@ -15,7 +15,10 @@ internal data class SignInUIState(
val isGoogleAuthEnabled: Boolean = false,
val isMicrosoftAuthEnabled: Boolean = false,
val isSocialAuthEnabled: Boolean = false,
+ val isBrowserLoginEnabled: Boolean = false,
+ val isBrowserRegistrationEnabled: Boolean = false,
val isLogistrationEnabled: Boolean = false,
val showProgress: Boolean = false,
val loginSuccess: Boolean = false,
+ val loginFailure: Boolean = false,
)
diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
index 4ab688e21..a93fb08f8 100644
--- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
@@ -15,6 +15,7 @@ import org.openedx.auth.R
import org.openedx.auth.data.model.AuthType
import org.openedx.auth.domain.interactor.AuthInteractor
import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.auth.presentation.sso.BrowserAuthHelper
import org.openedx.auth.presentation.sso.FacebookAuthHelper
import org.openedx.auth.presentation.sso.GoogleAuthHelper
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
@@ -42,6 +43,7 @@ class SignInViewModel(
private val facebookAuthHelper: FacebookAuthHelper,
private val googleAuthHelper: GoogleAuthHelper,
private val microsoftAuthHelper: MicrosoftAuthHelper,
+ private val browserAuthHelper: BrowserAuthHelper,
val config: Config,
val courseId: String?,
) : BaseViewModel() {
@@ -53,6 +55,8 @@ class SignInViewModel(
isFacebookAuthEnabled = config.getFacebookConfig().isEnabled(),
isGoogleAuthEnabled = config.getGoogleConfig().isEnabled(),
isMicrosoftAuthEnabled = config.getMicrosoftConfig().isEnabled(),
+ isBrowserLoginEnabled = config.isBrowserLoginEnabled(),
+ isBrowserRegistrationEnabled = config.isBrowserRegistrationEnabled(),
isSocialAuthEnabled = config.isSocialAuthEnabled(),
isLogistrationEnabled = config.isPreLoginExperienceEnabled(),
)
@@ -140,6 +144,17 @@ class SignInViewModel(
}
}
+ fun signInBrowser(activityContext: Activity) {
+ _uiState.update { it.copy(showProgress = true) }
+ viewModelScope.launch {
+ runCatching {
+ browserAuthHelper.signIn(activityContext)
+ }.onFailure {
+ logger.e { "Browser auth error: $it" }
+ }
+ }
+ }
+
fun signInMicrosoft(activityContext: Activity) {
_uiState.update { it.copy(showProgress = true) }
viewModelScope.launch {
@@ -155,6 +170,26 @@ class SignInViewModel(
}
}
+ fun signInAuthCode(authCode: String) {
+ _uiState.update { it.copy(showProgress = true) }
+ viewModelScope.launch {
+ runCatching {
+ interactor.loginAuthCode(authCode)
+ }
+ .onFailure {
+ logger.e { "OAuth2 code error: $it" }
+ onUnknownError()
+ _uiState.update { it.copy(loginFailure = true) }
+ }.onSuccess {
+ logger.d { "Browser login success" }
+ _uiState.update { it.copy(loginSuccess = true) }
+ setUserId()
+ analytics.userLoginEvent(AuthType.BROWSER.methodName)
+ _uiState.update { it.copy(showProgress = false) }
+ }
+ }
+ }
+
fun signUpClickedEvent() {
analytics.signUpClickedEvent()
}
diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt
index 2aed9869c..e49997e52 100644
--- a/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt
@@ -206,49 +206,55 @@ private fun AuthForm(
var password by rememberSaveable { mutableStateOf("") }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
- LoginTextField(
- modifier = Modifier
- .fillMaxWidth(),
- onValueChanged = {
- login = it
- })
+ if (!state.isBrowserLoginEnabled) {
+ LoginTextField(
+ modifier = Modifier
+ .fillMaxWidth(),
+ onValueChanged = {
+ login = it
+ })
- Spacer(modifier = Modifier.height(18.dp))
- PasswordTextField(
- modifier = Modifier
- .fillMaxWidth(),
- onValueChanged = {
- password = it
- },
- onPressDone = {
- onEvent(AuthEvent.SignIn(login = login, password = password))
- }
- )
+ Spacer(modifier = Modifier.height(18.dp))
+ PasswordTextField(
+ modifier = Modifier
+ .fillMaxWidth(),
+ onValueChanged = {
+ password = it
+ },
+ onPressDone = {
+ onEvent(AuthEvent.SignIn(login = login, password = password))
+ }
+ )
+ } else {
+ Spacer(modifier = Modifier.height(40.dp))
+ }
Row(
Modifier
.fillMaxWidth()
.padding(top = 20.dp, bottom = 36.dp)
) {
- if (state.isLogistrationEnabled.not()) {
+ if (!state.isBrowserLoginEnabled) {
+ if (state.isLogistrationEnabled.not()) {
+ Text(
+ modifier = Modifier.noRippleClickable {
+ onEvent(AuthEvent.RegisterClick)
+ },
+ text = stringResource(id = coreR.string.core_register),
+ color = MaterialTheme.appColors.primary,
+ style = MaterialTheme.appTypography.labelLarge
+ )
+ }
+ Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier.noRippleClickable {
- onEvent(AuthEvent.RegisterClick)
+ onEvent(AuthEvent.ForgotPasswordClick)
},
- text = stringResource(id = coreR.string.core_register),
+ text = stringResource(id = R.string.auth_forgot_password),
color = MaterialTheme.appColors.primary,
style = MaterialTheme.appTypography.labelLarge
)
}
- Spacer(modifier = Modifier.weight(1f))
- Text(
- modifier = Modifier.noRippleClickable {
- onEvent(AuthEvent.ForgotPasswordClick)
- },
- text = stringResource(id = R.string.auth_forgot_password),
- color = MaterialTheme.appColors.primary,
- style = MaterialTheme.appTypography.labelLarge
- )
}
if (state.showProgress) {
@@ -258,7 +264,11 @@ private fun AuthForm(
width = buttonWidth,
text = stringResource(id = coreR.string.core_sign_in),
onClick = {
- onEvent(AuthEvent.SignIn(login = login, password = password))
+ if(state.isBrowserLoginEnabled) {
+ onEvent(AuthEvent.SignInBrowser)
+ } else {
+ onEvent(AuthEvent.SignIn(login = login, password = password))
+ }
}
)
}
@@ -413,6 +423,24 @@ private fun SignInScreenPreview() {
}
}
+@Preview(uiMode = UI_MODE_NIGHT_NO)
+@Preview(uiMode = UI_MODE_NIGHT_YES)
+@Preview(name = "NEXUS_5_Light", device = Devices.NEXUS_5, uiMode = UI_MODE_NIGHT_NO)
+@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = UI_MODE_NIGHT_YES)
+@Composable
+private fun SignInUsingBrowserScreenPreview() {
+ OpenEdXTheme {
+ LoginScreen(
+ windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
+ state = SignInUIState().copy(
+ isBrowserLoginEnabled = true,
+ ),
+ uiMessage = null,
+ onEvent = {},
+ )
+ }
+}
+
@Preview(name = "NEXUS_9_Light", device = Devices.NEXUS_9, uiMode = UI_MODE_NIGHT_NO)
@Preview(name = "NEXUS_9_Night", device = Devices.NEXUS_9, uiMode = UI_MODE_NIGHT_YES)
diff --git a/auth/src/main/java/org/openedx/auth/presentation/sso/BrowserAuthHelper.kt b/auth/src/main/java/org/openedx/auth/presentation/sso/BrowserAuthHelper.kt
new file mode 100644
index 000000000..5822babd6
--- /dev/null
+++ b/auth/src/main/java/org/openedx/auth/presentation/sso/BrowserAuthHelper.kt
@@ -0,0 +1,32 @@
+package org.openedx.auth.presentation.sso
+
+import android.app.Activity
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.net.Uri
+import androidx.annotation.WorkerThread
+import androidx.browser.customtabs.CustomTabsIntent
+import org.openedx.core.config.Config
+import org.openedx.core.utils.Logger
+
+class BrowserAuthHelper(private val config: Config) {
+
+ private val logger = Logger(TAG)
+
+ @WorkerThread
+ suspend fun signIn(activityContext: Activity) {
+ logger.d { "Browser-based auth initiated" }
+ val uri = Uri.parse("${config.getApiHostURL()}/oauth2/authorize").buildUpon()
+ .appendQueryParameter("client_id", config.getOAuthClientId())
+ .appendQueryParameter("redirect_uri", "${activityContext.packageName}://oauth2Callback")
+ .appendQueryParameter("response_type", "code").build()
+ val intent =
+ CustomTabsIntent.Builder().setUrlBarHidingEnabled(true).setShowTitle(true).build()
+ intent.intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
+ logger.d { "Launching custom tab with ${uri.toString()}"}
+ intent.launchUrl(activityContext, uri)
+ }
+
+ private companion object {
+ const val TAG = "BrowserAuthHelper"
+ }
+}
diff --git a/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
index 9e5e13be0..ff2e53ab6 100644
--- a/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
+++ b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
@@ -24,6 +24,7 @@ import org.junit.rules.TestRule
import org.openedx.auth.R
import org.openedx.auth.domain.interactor.AuthInteractor
import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.auth.presentation.sso.BrowserAuthHelper
import org.openedx.auth.presentation.sso.FacebookAuthHelper
import org.openedx.auth.presentation.sso.GoogleAuthHelper
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
@@ -59,6 +60,7 @@ class SignInViewModelTest {
private val facebookAuthHelper = mockk()
private val googleAuthHelper = mockk()
private val microsoftAuthHelper = mockk()
+ private val browserAuthHelper = mockk()
private val invalidCredential = "Invalid credentials"
private val noInternet = "Slow or no internet connection"
@@ -82,6 +84,8 @@ class SignInViewModelTest {
every { config.getFacebookConfig() } returns FacebookConfig()
every { config.getGoogleConfig() } returns GoogleConfig()
every { config.getMicrosoftConfig() } returns MicrosoftConfig()
+ every { config.isBrowserLoginEnabled() } returns false
+ every { config.isBrowserRegistrationEnabled() } returns false
}
@After
@@ -105,6 +109,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
viewModel.login("", "")
@@ -134,6 +139,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
viewModel.login("acc@test.o", "")
@@ -165,6 +171,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
viewModel.login("acc@test.org", "")
@@ -195,6 +202,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
viewModel.login("acc@test.org", "ed")
@@ -227,6 +235,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
coEvery { interactor.login("acc@test.org", "edx") } returns Unit
@@ -260,6 +269,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
coEvery { interactor.login("acc@test.org", "edx") } throws UnknownHostException()
@@ -294,6 +304,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
coEvery { interactor.login("acc@test.org", "edx") } throws EdxError.InvalidGrantException()
@@ -328,6 +339,7 @@ class SignInViewModelTest {
googleAuthHelper = googleAuthHelper,
microsoftAuthHelper = microsoftAuthHelper,
config = config,
+ browserAuthHelper = browserAuthHelper,
courseId = "",
)
coEvery { interactor.login("acc@test.org", "edx") } throws IllegalStateException()
diff --git a/core/src/main/java/org/openedx/core/ApiConstants.kt b/core/src/main/java/org/openedx/core/ApiConstants.kt
index 091a09f42..2909bbc31 100644
--- a/core/src/main/java/org/openedx/core/ApiConstants.kt
+++ b/core/src/main/java/org/openedx/core/ApiConstants.kt
@@ -12,6 +12,7 @@ object ApiConstants {
const val URL_PASSWORD_RESET = "/password_reset/"
const val GRANT_TYPE_PASSWORD = "password"
+ const val GRANT_TYPE_CODE = "authorization_code"
const val TOKEN_TYPE_BEARER = "Bearer"
const val TOKEN_TYPE_JWT = "jwt"
@@ -23,4 +24,5 @@ object ApiConstants {
const val AUTH_TYPE_GOOGLE = "google-oauth2"
const val AUTH_TYPE_FB = "facebook"
const val AUTH_TYPE_MICROSOFT = "azuread-oauth2"
+ const val AUTH_TYPE_BROWSER = "browser"
}
diff --git a/core/src/main/java/org/openedx/core/config/Config.kt b/core/src/main/java/org/openedx/core/config/Config.kt
index 27aeee158..9332f8de7 100644
--- a/core/src/main/java/org/openedx/core/config/Config.kt
+++ b/core/src/main/java/org/openedx/core/config/Config.kt
@@ -95,6 +95,14 @@ class Config(context: Context) {
return getBoolean(COURSE_UNIT_PROGRESS_ENABLED, false)
}
+ fun isBrowserLoginEnabled(): Boolean {
+ return getBoolean(BROWSER_LOGIN, false)
+ }
+
+ fun isBrowserRegistrationEnabled(): Boolean {
+ return getBoolean(BROWSER_REGISTRATION, false)
+ }
+
private fun getString(key: String, defaultValue: String): String {
val element = getObject(key)
return if (element != null) {
@@ -143,6 +151,8 @@ class Config(context: Context) {
private const val GOOGLE = "GOOGLE"
private const val MICROSOFT = "MICROSOFT"
private const val PRE_LOGIN_EXPERIENCE_ENABLED = "PRE_LOGIN_EXPERIENCE_ENABLED"
+ private const val BROWSER_LOGIN = "BROWSER_LOGIN"
+ private const val BROWSER_REGISTRATION = "BROWSER_REGISTRATION"
private const val DISCOVERY = "DISCOVERY"
private const val COURSE_NESTED_LIST_ENABLED = "COURSE_NESTED_LIST_ENABLED"
private const val COURSE_BANNER_ENABLED = "COURSE_BANNER_ENABLED"