Skip to content

Commit 135a305

Browse files
committed
feat: add saml sso
1 parent eb3f8a9 commit 135a305

File tree

19 files changed

+683
-165
lines changed

19 files changed

+683
-165
lines changed

app/src/main/java/org/openedx/app/AppRouter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.openedx.core.FragmentViewType
1414
import org.openedx.core.presentation.course.CourseViewMode
1515
import org.openedx.core.presentation.global.appupgrade.AppUpgradeRouter
1616
import org.openedx.core.presentation.global.appupgrade.UpgradeRequiredFragment
17+
import org.openedx.core.presentation.global.webview.SSOWebContentFragment
1718
import org.openedx.core.presentation.global.webview.WebContentFragment
1819
import org.openedx.core.presentation.settings.video.VideoQualityFragment
1920
import org.openedx.core.presentation.settings.video.VideoQualityType
@@ -432,6 +433,13 @@ class AppRouter :
432433
)
433434
}
434435

436+
override fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String) {
437+
replaceFragmentWithBackStack(
438+
fm,
439+
SSOWebContentFragment.newInstance(title = title, url = url)
440+
)
441+
}
442+
435443
override fun navigateToManageAccount(fm: FragmentManager) {
436444
replaceFragmentWithBackStack(fm, ManageAccountFragment())
437445
}

app/src/main/java/org/openedx/app/di/ScreenModule.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ val screenModule = module {
121121
get(),
122122
get(),
123123
get(),
124+
get(),
124125
courseId,
125126
infoType,
126127
)

auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ class AuthRepository(
3131
.processAuthResponse()
3232
}
3333

34+
suspend fun ssoLogin(
35+
jwtToken: String
36+
) {
37+
if (preferencesManager.accessToken.isBlank() ||
38+
preferencesManager.refreshToken.isBlank()){
39+
preferencesManager.accessToken = jwtToken
40+
preferencesManager.refreshToken = jwtToken
41+
}
42+
val user = api.getProfile()
43+
preferencesManager.user = user
44+
}
45+
3446
suspend fun socialLogin(token: String?, authType: AuthType) {
3547
require(!token.isNullOrBlank()) { "Token is null" }
3648
api.exchangeAccessToken(

auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ class AuthInteractor(private val repository: AuthRepository) {
1414
repository.login(username, password)
1515
}
1616

17+
suspend fun ssoLogin(
18+
jwtToken: String
19+
) {
20+
repository.ssoLogin(jwtToken)
21+
}
22+
1723
suspend fun loginSocial(token: String?, authType: AuthType) {
1824
repository.socialLogin(token, authType)
1925
}

auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,7 @@ interface AuthRouter {
2727

2828
fun navigateToWebContent(fm: FragmentManager, title: String, url: String)
2929

30+
fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String)
31+
3032
fun clearBackStack(fm: FragmentManager)
3133
}

auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.ui.platform.ComposeView
1111
import androidx.compose.ui.platform.ViewCompositionStrategy
1212
import androidx.core.os.bundleOf
1313
import androidx.fragment.app.Fragment
14+
import androidx.fragment.app.setFragmentResultListener
1415
import org.koin.androidx.viewmodel.ext.android.viewModel
1516
import org.koin.core.parameter.parametersOf
1617
import org.openedx.auth.data.model.AuthType
@@ -43,13 +44,17 @@ class SignInFragment : Fragment() {
4344
val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState(null)
4445

4546
if (appUpgradeEvent == null) {
47+
setFragmentResultListener("requestKey") { requestKey, bundle ->
48+
viewModel.ssoLogin(token = requestKey)
49+
}
4650
LoginScreen(
4751
windowSize = windowSize,
4852
state = state,
4953
uiMessage = uiMessage,
5054
onEvent = { event ->
5155
when (event) {
5256
is AuthEvent.SignIn -> viewModel.login(event.login, event.password)
57+
is AuthEvent.SsoSignIn -> viewModel.ssoClicked(parentFragmentManager)
5358
is AuthEvent.SocialSignIn -> viewModel.socialAuth(
5459
this@SignInFragment,
5560
event.authType
@@ -105,6 +110,7 @@ class SignInFragment : Fragment() {
105110

106111
internal sealed interface AuthEvent {
107112
data class SignIn(val login: String, val password: String) : AuthEvent
113+
data class SsoSignIn(val jwtToken: String) : AuthEvent
108114
data class SocialSignIn(val authType: AuthType) : AuthEvent
109115
data class OpenLink(val links: Map<String, String>, val link: String) : AuthEvent
110116
object RegisterClick : AuthEvent

auth/src/main/java/org/openedx/auth/presentation/signin/SignInUIState.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import org.openedx.core.domain.model.RegistrationField
1313
* @param loginSuccess is login succeed
1414
*/
1515
internal data class SignInUIState(
16+
val isLoginRegistrationFormEnabled: Boolean = true,
17+
val isSSOLoginEnabled: Boolean = false,
18+
val ssoButtonTitle: String = "",
19+
val isSSODefaultLoginButton: Boolean = false,
1620
val isFacebookAuthEnabled: Boolean = false,
1721
val isGoogleAuthEnabled: Boolean = false,
1822
val isMicrosoftAuthEnabled: Boolean = false,

auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.openedx.auth.presentation.signin
22

3+
import android.content.res.Resources
34
import androidx.fragment.app.Fragment
45
import androidx.fragment.app.FragmentManager
56
import androidx.lifecycle.LiveData
@@ -48,6 +49,7 @@ class SignInViewModel(
4849
private val appNotifier: AppNotifier,
4950
private val analytics: AuthAnalytics,
5051
private val oAuthHelper: OAuthHelper,
52+
private val configuration: Config,
5153
private val router: AuthRouter,
5254
private val whatsNewGlobalManager: WhatsNewGlobalManager,
5355
private val calendarPreferences: CalendarPreferences,
@@ -62,6 +64,10 @@ class SignInViewModel(
6264

6365
private val _uiState = MutableStateFlow(
6466
SignInUIState(
67+
isLoginRegistrationFormEnabled = config.isLoginRegistrationEnabled(),
68+
isSSOLoginEnabled = config.isSSOLoginEnabled(),
69+
ssoButtonTitle = config.getSSOButtonTitle(key = Resources.getSystem().getConfiguration().locales[0].language.uppercase(), ""),
70+
isSSODefaultLoginButton = config.isSSODefaultLoginButton(),
6571
isFacebookAuthEnabled = config.getFacebookConfig().isEnabled(),
6672
isGoogleAuthEnabled = config.getGoogleConfig().isEnabled(),
6773
isMicrosoftAuthEnabled = config.getMicrosoftConfig().isEnabled(),
@@ -135,6 +141,50 @@ class SignInViewModel(
135141
}
136142
}
137143

144+
fun ssoClicked(fragmentManager: FragmentManager) {
145+
router.navigateToSSOWebContent(
146+
fm = fragmentManager,
147+
title = resourceManager.getString(org.openedx.core.R.string.core_sso_sign_in),
148+
url = configuration.getSSOURL(),
149+
)
150+
}
151+
152+
fun ssoLogin(token: String) {
153+
logEvent(AuthAnalyticsEvent.USER_SIGN_IN_CLICKED)
154+
155+
156+
_uiState.update { it.copy(showProgress = true) }
157+
viewModelScope.launch {
158+
try {
159+
interactor.ssoLogin("JWT $token")
160+
_uiState.update { it.copy(loginSuccess = true) }
161+
162+
setUserId()
163+
logEvent(
164+
AuthAnalyticsEvent.SIGN_IN_SUCCESS,
165+
buildMap {
166+
put(
167+
AuthAnalyticsKey.METHOD.key,
168+
AuthType.PASSWORD.methodName.lowercase()
169+
)
170+
}
171+
)
172+
} catch (e: Exception) {
173+
if (e is EdxError.InvalidGrantException) {
174+
_uiMessage.value =
175+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_invalid_grant))
176+
} else if (e.isInternetError()) {
177+
_uiMessage.value =
178+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_no_connection))
179+
} else {
180+
_uiMessage.value =
181+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_unknown_error))
182+
}
183+
}
184+
_uiState.update { it.copy(showProgress = false) }
185+
}
186+
}
187+
138188
private fun collectAppUpgradeEvent() {
139189
viewModelScope.launch {
140190
appNotifier.notifier.collect { event ->

0 commit comments

Comments
 (0)