Skip to content

Commit

Permalink
added auth0 authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
aman-alfresco committed Jul 5, 2024
1 parent 2729e4a commit 93ccf8e
Show file tree
Hide file tree
Showing 17 changed files with 338 additions and 135 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,6 @@ lint/reports/
/gradle/libs.versions_app.toml
app/google-services_1.json
app/google-services_2.json
/build-other.gradle
/gradle/libs.versions-other.toml
/alfresco-auth/
19 changes: 17 additions & 2 deletions account/src/main/kotlin/com/alfresco/content/account/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ data class Account(
val displayName: String? = null,
val email: String? = null,
val myFiles: String? = null,
val hostName: String? = null,
val clientId: String? = null,
) {
companion object {
private const val displayNameKey = "displayName"
Expand All @@ -25,6 +27,8 @@ data class Account(
private const val authConfigKey = "config"
private const val serverKey = "server"
private const val myFilesKey = "myFiles"
private const val hostNameKey = "auth0HostName"
private const val clientIdKey = "auth0ClientId"

fun createAccount(
context: Context,
Expand All @@ -36,6 +40,8 @@ data class Account(
displayName: String,
email: String,
myFiles: String,
hostName: String,
clientId: String,
) {
val sharedSecure = SecureSharedPreferencesManager(context)

Expand All @@ -46,10 +52,12 @@ data class Account(
b.putString(displayNameKey, KEY_DISPLAY_NAME)
b.putString(emailKey, KEY_EMAIL)
b.putString(myFilesKey, myFiles)
b.putString(hostNameKey, hostName)
b.putString(clientIdKey, clientId)
val acc = AndroidAccount(id, context.getString(R.string.android_auth_account_type))

// Save credentials securely using the SecureSharedPreferencesManager
sharedSecure.saveCredentials(email, authState, displayName)
sharedSecure.saveCredentials(email, authState, displayName, hostName, clientId)

AccountManager.get(context).addAccountExplicitly(acc, KEY_PASSWORD, b)
}
Expand All @@ -72,18 +80,22 @@ data class Account(
displayName: String,
email: String,
myFiles: String,
hostName: String,
clientId: String,
) {
val sharedSecure = SecureSharedPreferencesManager(context)
val am = AccountManager.get(context)
val acc = getAndroidAccount(context)

// Save credentials securely using the SecureSharedPreferencesManager
sharedSecure.saveCredentials(email, authState, displayName)
sharedSecure.saveCredentials(email, authState, displayName, hostName, clientId)

am.setPassword(acc, KEY_PASSWORD)
am.setUserData(acc, displayNameKey, KEY_DISPLAY_NAME)
am.setUserData(acc, emailKey, KEY_EMAIL)
am.setUserData(acc, myFilesKey, myFiles)
am.setUserData(acc, hostNameKey, hostName)
am.setUserData(acc, clientIdKey, clientId)

if (acc?.name != id) {
am.renameAccount(acc, id, null, null)
Expand All @@ -105,6 +117,7 @@ data class Account(
val secureCredentials = sharedSecure.getSavedCredentials()
if (accountList.isNotEmpty() && secureCredentials != null) {
val acc = accountList[0]
val secureAuth0 = sharedSecure.getSavedAuth0Data()
return Account(
acc.name,
secureCredentials.second,
Expand All @@ -114,6 +127,8 @@ data class Account(
secureCredentials.third,
secureCredentials.first,
am.getUserData(acc, myFilesKey),
secureAuth0?.first,
secureAuth0?.second,
)
}
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class SecureSharedPreferencesManager(private val context: Context) {
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()

fun saveCredentials(email: String, password: String, displayName: String) {
fun saveCredentials(email: String, password: String, displayName: String, auth0HostName: String, auth0ClientId: String) {
try {
val encryptedSharedPreferences = EncryptedSharedPreferences.create(
context,
Expand All @@ -28,6 +28,8 @@ class SecureSharedPreferencesManager(private val context: Context) {
.putString(KEY_EMAIL, email)
.putString(KEY_DISPLAY_NAME, displayName)
.putString(KEY_PASSWORD, password)
.putString(KEY_HOST_NAME, auth0HostName)
.putString(KEY_CLIENT_ID, auth0ClientId)
.apply()

Log.d(TAG, "Credentials saved securely")
Expand Down Expand Up @@ -80,11 +82,36 @@ class SecureSharedPreferencesManager(private val context: Context) {
return null
}

fun getSavedAuth0Data(): Pair<String, String>? {
try {
val encryptedSharedPreferences = EncryptedSharedPreferences.create(
context,
KEY_PREF_NAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
)

val hostName = encryptedSharedPreferences.getString(KEY_HOST_NAME, null)
val clientId = encryptedSharedPreferences.getString(KEY_CLIENT_ID, null)

if (hostName != null && clientId != null) {
return Pair(hostName, clientId)
}
} catch (e: Exception) {
Log.e(TAG, "Error retrieving credentials: ${e.message}")
}

return null
}

companion object {
const val KEY_DISPLAY_NAME = "display_name"
const val KEY_EMAIL = "email"
const val KEY_PASSWORD = "password"
const val KEY_PREF_NAME = "secure_prefs"
const val KEY_HOST_NAME = "host_name"
const val KEY_CLIENT_ID = "client_id"

val TAG: String = SecureSharedPreferencesManager::class.java.simpleName
}
Expand Down
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ android {
versionName "1.9.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

manifestPlaceholders = [auth0Domain: "@string/com_auth0_domain", auth0Scheme: "@string/com_auth0_scheme"]
}

signingConfigs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class LoginActivity : com.alfresco.auth.activity.LoginActivity() {
navigateToMain()
}
} catch (ex: Exception) {
ex.printStackTrace()
onError(R.string.auth_error_wrong_credentials)
}
}
Expand All @@ -53,6 +54,8 @@ class LoginActivity : com.alfresco.auth.activity.LoginActivity() {
person.displayName ?: "",
person.email,
myFiles,
credentials.hostName,
credentials.clientId,
)
} else {
val current = Account.getAccount(applicationContext)
Expand All @@ -69,6 +72,8 @@ class LoginActivity : com.alfresco.auth.activity.LoginActivity() {
person.displayName ?: "",
person.email,
myFiles,
credentials.hostName,
credentials.clientId,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
private fun logout() {
val acc = SessionManager.requireSession.account
val i = Intent(context, LogoutActivity::class.java)
i.putExtra(LogoutViewModel.EXTRA_HOST_NAME, acc.hostName)
i.putExtra(LogoutViewModel.EXTRA_CLIENT_ID, acc.clientId)
i.putExtra(LogoutViewModel.EXTRA_AUTH_TYPE, acc.authType)
i.putExtra(LogoutViewModel.EXTRA_AUTH_CONFIG, acc.authConfig)
i.putExtra(LogoutViewModel.EXTRA_AUTH_STATE, acc.authState)
Expand Down
1 change: 1 addition & 0 deletions auth/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dependencies {
implementation project(':theme')
implementation project(':data')
implementation project(':common')
implementation project(':component')

implementation libs.kotlin.stdlib

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ abstract class LoginActivity : AuthenticationActivity<LoginViewModel>() {
LoginViewModel.Step.InputIdentityServer -> InputIdentityFragment.with(this).display()
LoginViewModel.Step.InputAppServer -> InputServerFragment.with(this).display()
LoginViewModel.Step.EnterBasicCredentials -> BasicAuthFragment.with(this).display()
LoginViewModel.Step.EnterPkceCredentials -> viewModel.ssoLogin()
LoginViewModel.Step.EnterPkceCredentials, LoginViewModel.Step.EnterAuth0Credentials -> viewModel.ssoLogin()
LoginViewModel.Step.Cancelled -> finish()
}
}
Expand Down
66 changes: 60 additions & 6 deletions auth/src/main/kotlin/com/alfresco/auth/activity/LoginViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import androidx.lifecycle.viewModelScope
import com.alfresco.android.aims.R
import com.alfresco.auth.AuthConfig
import com.alfresco.auth.AuthType
import com.alfresco.auth.config.defaultConfig
import com.alfresco.auth.config.defaultAuth0Config
import com.alfresco.auth.config.defaultKeycloakConfig
import com.alfresco.auth.data.LiveEvent
import com.alfresco.auth.data.MutableLiveEvent
import com.alfresco.auth.ui.AuthenticationViewModel
import com.alfresco.content.data.OptionsModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class LoginViewModel(
private val applicationContext: Context,
authType: AuthType?,
val authType: AuthType?,
authState: String?,
authConfig: AuthConfig?,
endpoint: String?,
Expand Down Expand Up @@ -58,7 +60,9 @@ class LoginViewModel(
val canonicalApplicationUrl: String
get() {
return previousAppEndpoint
?: discoveryService.contentServiceUrl(applicationUrl.value!!).toString()
?: if (authType == AuthType.PKCE) {
discoveryService.contentServiceUrl(applicationUrl.value!!).toString()
} else discoveryService.oidcUrl(applicationUrl.value!!).toString()
}

// Used for display purposes
Expand Down Expand Up @@ -126,6 +130,18 @@ class LoginViewModel(
}
}

AuthType.OIDC -> {
viewModelScope.launch {
val isOIDCInstalled = withContext(Dispatchers.IO) {
discoveryService.isOIDCInstalled(identityUrl.value ?: "")
}
if (isOIDCInstalled) {
applicationUrl.value = identityUrl.value
moveToStep(Step.EnterAuth0Credentials)
}
}
}

AuthType.BASIC -> {
moveToStep(Step.EnterBasicCredentials)
}
Expand Down Expand Up @@ -172,7 +188,7 @@ class LoginViewModel(
val sharedPrefs = applicationContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
val configJson = sharedPrefs.getString(SHARED_PREFS_CONFIG_KEY, null) ?: ""

authConfig = AuthConfig.jsonDeserialize(configJson) ?: AuthConfig.defaultConfig
authConfig = AuthConfig.jsonDeserialize(configJson) ?: AuthConfig.defaultKeycloakConfig
}

fun saveConfigChanges() {
Expand All @@ -199,15 +215,19 @@ class LoginViewModel(
when (step) {
Step.InputIdentityServer -> {
}

Step.InputAppServer -> {
applicationUrl.value = ""
}

Step.EnterBasicCredentials -> {
// Assume application url is the same as identity for basic auth
applicationUrl.value = identityUrl.value
}
Step.EnterPkceCredentials -> {

Step.EnterPkceCredentials, Step.EnterAuth0Credentials -> {
}

Step.Cancelled -> {
}
}
Expand All @@ -220,6 +240,7 @@ class LoginViewModel(
InputAppServer,
EnterBasicCredentials,
EnterPkceCredentials,
EnterAuth0Credentials,
Cancelled,
}

Expand Down Expand Up @@ -287,16 +308,32 @@ class LoginViewModel(
private lateinit var source: AuthConfig
private val _changed = MediatorLiveData<Boolean>()

val authTypeValue = MutableLiveData<String>()
val authTypeName = MutableLiveData<String>()
val https = MutableLiveData<Boolean>()
val port = MutableLiveData<String>()
val contentServicePath = MutableLiveData<String>()
val realm = MutableLiveData<String>()
val clientId = MutableLiveData<String>()
private var redirectUrl: String = ""
private var scheme: String = ""

val changed: LiveData<Boolean> get() = _changed

val listAuthType = listOf(
OptionsModel(
id = AuthType.PKCE.value,
name = "Keycloak",
),
OptionsModel(
id = AuthType.OIDC.value,
name = "Auth0",
),
)

init {
_changed.addSource(authTypeValue, this::onChange)
_changed.addSource(authTypeName, this::onChange)
_changed.addSource(https, this::onChange)
_changed.addSource(port, this::onChange)
_changed.addSource(contentServicePath, this::onChange)
Expand All @@ -316,6 +353,15 @@ class LoginViewModel(
port.value = if (https.value == true) DEFAULT_HTTPS_PORT else DEFAULT_HTTP_PORT
}

fun onAuthChange(authName: String, authValue: String) {
authTypeName.value = authName
authTypeValue.value = authValue
when (authValue.lowercase()) {
AuthType.PKCE.value.lowercase() -> load(AuthConfig.defaultKeycloakConfig)
AuthType.OIDC.value.lowercase() -> load(AuthConfig.defaultAuth0Config)
}
}

private fun onChange(@Suppress("UNUSED_PARAMETER") value: Boolean) {
onChange()
}
Expand All @@ -335,16 +381,22 @@ class LoginViewModel(

fun resetToDefaultConfig() {
// Source is not changed as resetting to default does not commit changes
load(AuthConfig.defaultConfig)
when (authTypeValue.value?.lowercase()) {
AuthType.PKCE.value.lowercase() -> load(AuthConfig.defaultKeycloakConfig)
AuthType.OIDC.value.lowercase() -> load(AuthConfig.defaultAuth0Config)
}
}

private fun load(config: AuthConfig) {
authTypeName.value = listAuthType.find { it.id == config.authType }?.name ?: ""
authTypeValue.value = config.authType
https.value = config.https
port.value = config.port
contentServicePath.value = config.contentServicePath
realm.value = config.realm
clientId.value = config.clientId
redirectUrl = config.redirectUrl
scheme = config.scheme
onChange()
}

Expand All @@ -356,6 +408,8 @@ class LoginViewModel(
realm = realm.value ?: "",
clientId = clientId.value ?: "",
redirectUrl = redirectUrl,
scheme = scheme,
authType = authTypeValue.value ?: "",
)
}

Expand Down
Loading

0 comments on commit 93ccf8e

Please sign in to comment.