Skip to content

Commit 92cb74e

Browse files
committed
feat: Support for login and registration via a browser custom tab
This change adds support for logging in and registering a new account using the browser. This can be useful for cases where the only way to log into the instatance is via a custom third-party auth provider.
1 parent e61523e commit 92cb74e

File tree

27 files changed

+324
-39
lines changed

27 files changed

+324
-39
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848

4949
<category android:name="android.intent.category.LAUNCHER" />
5050
</intent-filter>
51+
<intent-filter>
52+
<action android:name="android.intent.action.VIEW" />
53+
<category android:name="android.intent.category.DEFAULT" />
54+
<category android:name="android.intent.category.BROWSABLE" />
55+
<data android:scheme="${applicationId}" />
56+
</intent-filter>
5157

5258
<!-- Branch URI Scheme -->
5359
<intent-filter>

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.openedx.app
33
import android.content.Intent
44
import android.content.res.Configuration
55
import android.graphics.Color
6+
import android.net.Uri
67
import android.os.Bundle
78
import android.view.View
89
import android.view.WindowManager
@@ -64,6 +65,14 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
6465
private var _insetCutout = 0
6566

6667
private var _windowSize = WindowSize(WindowType.Compact, WindowType.Compact)
68+
private val authCode: String?
69+
get() {
70+
val data = intent?.data
71+
if (data is Uri && data.scheme == BuildConfig.APPLICATION_ID && data.host == "oauth2Callback") {
72+
return data.getQueryParameter("code")
73+
}
74+
return null
75+
}
6776

6877
private val branchCallback =
6978
BranchUniversalReferralInitListener { branchUniversalObject, _, error ->
@@ -141,10 +150,15 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
141150
if (savedInstanceState == null) {
142151
when {
143152
corePreferencesManager.user == null -> {
144-
if (viewModel.isLogistrationEnabled) {
153+
val authCode = authCode;
154+
if (viewModel.isLogistrationEnabled && authCode == null) {
145155
addFragment(LogistrationFragment())
146156
} else {
147-
addFragment(SignInFragment())
157+
val bundle = Bundle()
158+
bundle.putString("auth_code", authCode)
159+
val fragment = SignInFragment()
160+
fragment.arguments = bundle
161+
addFragment(fragment)
148162
}
149163
}
150164

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.openedx.app.room.DatabaseManager
2323
import org.openedx.auth.presentation.AgreementProvider
2424
import org.openedx.auth.presentation.AuthAnalytics
2525
import org.openedx.auth.presentation.AuthRouter
26+
import org.openedx.auth.presentation.sso.BrowserAuthHelper
2627
import org.openedx.auth.presentation.sso.FacebookAuthHelper
2728
import org.openedx.auth.presentation.sso.GoogleAuthHelper
2829
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
@@ -202,6 +203,7 @@ val appModule = module {
202203
factory { FacebookAuthHelper() }
203204
factory { GoogleAuthHelper(get()) }
204205
factory { MicrosoftAuthHelper() }
206+
factory { BrowserAuthHelper(get()) }
205207
factory { OAuthHelper(get(), get(), get()) }
206208

207209
factory { FileUtil(get(), get<ResourceManager>().getString(R.string.app_name)) }

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ val screenModule = module {
100100
get(),
101101
get(),
102102
get(),
103+
get(),
103104
)
104105
}
105106

@@ -118,6 +119,7 @@ val screenModule = module {
118119
get(),
119120
get(),
120121
get(),
122+
get(),
121123
courseId,
122124
infoType,
123125
)

auth/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ android {
5454
dependencies {
5555
implementation project(path: ':core')
5656

57+
implementation 'androidx.browser:browser:1.7.0'
5758
implementation "androidx.credentials:credentials:1.3.0"
5859
implementation "androidx.credentials:credentials-play-services-auth:1.3.0"
5960
implementation "com.facebook.android:facebook-login:16.2.0"

auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ interface AuthApi {
3232
@Field("asymmetric_jwt") isAsymmetricJwt: Boolean = true,
3333
): AuthResponse
3434

35+
@FormUrlEncoded
36+
@POST(ApiConstants.URL_ACCESS_TOKEN)
37+
suspend fun getAccessTokenFromCode(
38+
@Field("grant_type") grantType: String,
39+
@Field("client_id") clientId: String,
40+
@Field("code") code: String,
41+
@Field("redirect_uri") redirectUri: String
42+
): AuthResponse
43+
3544
@FormUrlEncoded
3645
@POST(ApiConstants.URL_ACCESS_TOKEN)
3746
fun refreshAccessToken(

auth/src/main/java/org/openedx/auth/data/model/AuthType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ enum class AuthType(val postfix: String, val methodName: String) {
1313
GOOGLE(ApiConstants.AUTH_TYPE_GOOGLE, "Google"),
1414
FACEBOOK(ApiConstants.AUTH_TYPE_FB, "Facebook"),
1515
MICROSOFT(ApiConstants.AUTH_TYPE_MICROSOFT, "Microsoft"),
16+
BROWSER(ApiConstants.AUTH_TYPE_BROWSER, "Browser")
1617
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ class AuthRepository(
4343
.processAuthResponse()
4444
}
4545

46+
suspend fun browserAuthCodeLogin(code: String) {
47+
api.getAccessTokenFromCode(
48+
grantType = ApiConstants.GRANT_TYPE_CODE,
49+
clientId = config.getOAuthClientId(),
50+
code = code,
51+
redirectUri = "${config.getApplicationID()}://oauth2Callback"
52+
).mapToDomain().processAuthResponse()
53+
}
54+
4655
suspend fun getRegistrationFields(): List<RegistrationField> {
4756
return api.getRegistrationFields().fields?.map { it.mapToDomain() } ?: emptyList()
4857
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class AuthInteractor(private val repository: AuthRepository) {
1818
repository.socialLogin(token, authType)
1919
}
2020

21+
suspend fun loginAuthCode(authCode: String) {
22+
repository.browserAuthCodeLogin(authCode)
23+
}
24+
2125
suspend fun getRegistrationFields(): List<RegistrationField> {
2226
return repository.getRegistrationFields()
2327
}

auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import org.openedx.core.ui.theme.OpenEdXTheme
5050
import org.openedx.core.ui.theme.appColors
5151
import org.openedx.core.ui.theme.appTypography
5252
import org.openedx.core.ui.theme.compose.LogistrationLogoView
53+
import org.openedx.foundation.utils.UrlUtils
5354

5455
class LogistrationFragment : Fragment() {
5556

@@ -67,10 +68,23 @@ class LogistrationFragment : Fragment() {
6768
OpenEdXTheme {
6869
LogistrationScreen(
6970
onSignInClick = {
70-
viewModel.navigateToSignIn(parentFragmentManager)
71+
if(viewModel.isBrowserLoginEnabled) {
72+
viewModel.signInBrowser(requireActivity())
73+
} else {
74+
viewModel.navigateToSignIn(parentFragmentManager)
75+
}
76+
7177
},
7278
onRegisterClick = {
73-
viewModel.navigateToSignUp(parentFragmentManager)
79+
if (viewModel.isBrowserRegistrationEnabled) {
80+
UrlUtils.openInBrowser(
81+
activity = context,
82+
apiHostUrl = viewModel.apiHostUrl,
83+
url = "/register",
84+
)
85+
} else {
86+
viewModel.navigateToSignUp(parentFragmentManager)
87+
}
7488
},
7589
onSearchClick = { querySearch ->
7690
viewModel.navigateToDiscovery(parentFragmentManager, querySearch)

0 commit comments

Comments
 (0)