Skip to content

Commit 25abc04

Browse files
committed
feat: add single sing on by saml
1 parent ae777dd commit 25abc04

File tree

19 files changed

+565
-75
lines changed

19 files changed

+565
-75
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
@@ -12,6 +12,7 @@ import org.openedx.core.FragmentViewType
1212
import org.openedx.core.presentation.course.CourseViewMode
1313
import org.openedx.core.presentation.global.app_upgrade.AppUpgradeRouter
1414
import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment
15+
import org.openedx.core.presentation.global.webview.SSOWebContentFragment
1516
import org.openedx.core.presentation.global.webview.WebContentFragment
1617
import org.openedx.core.presentation.settings.VideoQualityFragment
1718
import org.openedx.core.presentation.settings.VideoQualityType
@@ -367,6 +368,13 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
367368
)
368369
}
369370

371+
override fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String) {
372+
replaceFragmentWithBackStack(
373+
fm,
374+
SSOWebContentFragment.newInstance(title = title, url = url)
375+
)
376+
}
377+
370378
override fun navigateToManageAccount(fm: FragmentManager) {
371379
replaceFragmentWithBackStack(fm, ManageAccountFragment())
372380
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ val screenModule = module {
9292
get(),
9393
get(),
9494
get(),
95+
get(),
9596
courseId,
9697
infoType,
9798
)

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
if (token.isNullOrBlank()) throw IllegalArgumentException("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
@@ -22,5 +22,7 @@ interface AuthRouter {
2222

2323
fun navigateToWebContent(fm: FragmentManager, title: String, url: String)
2424

25+
fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String)
26+
2527
fun clearBackStack(fm: FragmentManager)
2628
}

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
@@ -45,6 +46,7 @@ class SignInViewModel(
4546
private val appUpgradeNotifier: AppUpgradeNotifier,
4647
private val analytics: AuthAnalytics,
4748
private val oAuthHelper: OAuthHelper,
49+
private val configuration: Config,
4850
private val router: AuthRouter,
4951
private val whatsNewGlobalManager: WhatsNewGlobalManager,
5052
agreementProvider: AgreementProvider,
@@ -57,6 +59,10 @@ class SignInViewModel(
5759

5860
private val _uiState = MutableStateFlow(
5961
SignInUIState(
62+
isLoginRegistrationFormEnabled = config.isLoginRegistrationEnabled(),
63+
isSSOLoginEnabled = config.isSSOLoginEnabled(),
64+
ssoButtonTitle = config.getSSOButtonTitle(key = Resources.getSystem().getConfiguration().locales[0].language.uppercase(), ""),
65+
isSSODefaultLoginButton = config.isSSODefaultLoginButton(),
6066
isFacebookAuthEnabled = config.getFacebookConfig().isEnabled(),
6167
isGoogleAuthEnabled = config.getGoogleConfig().isEnabled(),
6268
isMicrosoftAuthEnabled = config.getMicrosoftConfig().isEnabled(),
@@ -123,6 +129,50 @@ class SignInViewModel(
123129
}
124130
}
125131

132+
fun ssoClicked(fragmentManager: FragmentManager) {
133+
router.navigateToSSOWebContent(
134+
fm = fragmentManager,
135+
title = resourceManager.getString(org.openedx.core.R.string.core_sso_sign_in),
136+
url = configuration.getSSOURL(),
137+
)
138+
}
139+
140+
fun ssoLogin(token: String) {
141+
logEvent(AuthAnalyticsEvent.USER_SIGN_IN_CLICKED)
142+
143+
144+
_uiState.update { it.copy(showProgress = true) }
145+
viewModelScope.launch {
146+
try {
147+
interactor.ssoLogin("JWT $token")
148+
_uiState.update { it.copy(loginSuccess = true) }
149+
150+
setUserId()
151+
logEvent(
152+
AuthAnalyticsEvent.SIGN_IN_SUCCESS,
153+
buildMap {
154+
put(
155+
AuthAnalyticsKey.METHOD.key,
156+
AuthType.PASSWORD.methodName.lowercase()
157+
)
158+
}
159+
)
160+
} catch (e: Exception) {
161+
if (e is EdxError.InvalidGrantException) {
162+
_uiMessage.value =
163+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_invalid_grant))
164+
} else if (e.isInternetError()) {
165+
_uiMessage.value =
166+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_no_connection))
167+
} else {
168+
_uiMessage.value =
169+
UIMessage.SnackBarMessage(resourceManager.getString(CoreRes.string.core_error_unknown_error))
170+
}
171+
}
172+
_uiState.update { it.copy(showProgress = false) }
173+
}
174+
}
175+
126176
private fun collectAppUpgradeEvent() {
127177
viewModelScope.launch {
128178
appUpgradeNotifier.notifier.collect { event ->

0 commit comments

Comments
 (0)