Skip to content

Commit

Permalink
added secured parameter for Auth0
Browse files Browse the repository at this point in the history
  • Loading branch information
aman-alfresco committed Jul 5, 2024
1 parent c7fc20d commit e3845c1
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 64 deletions.
11 changes: 8 additions & 3 deletions auth/src/main/java/com/alfresco/auth/AuthConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ data class AuthConfig(
var contentServicePath: String,

/**
* Path to content service
* scheme for Auth0
*/
var scheme: String = "",
/**
* selected AuthType
*/
var scheme: String = ""
var authType: String = ""
) {
/**
* Convenience method for JSON serialization.
Expand All @@ -56,7 +60,8 @@ data class AuthConfig(
/**
* Convenience method for deserializing a JSON string representation.
*/
@JvmStatic fun jsonDeserialize(str: String): AuthConfig? {
@JvmStatic
fun jsonDeserialize(str: String): AuthConfig? {
return try {
Json.decodeFromString(serializer(), str)
} catch (ex: SerializationException) {
Expand Down
18 changes: 17 additions & 1 deletion auth/src/main/java/com/alfresco/auth/AuthInterceptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,23 @@ class AuthInterceptor(

private inner class OIDCProvider (val accessToken : String) : Provider{
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(AuthType.OIDC, accessToken)
val response = chain.proceed(AuthType.OIDC, accessToken)

// When unauthorized try to refresh
// if (response.code == HTTP_RESPONSE_401_UNAUTHORIZED) {
// val newState = refreshTokenNow()
//
// if (newState != null) {
// response.close()
// response = chain.proceed(AuthType.PKCE, newState.accessToken)
// }
// }

// If still error notify listener of failure
if (response.code == HTTP_RESPONSE_401_UNAUTHORIZED) {
listener?.onAuthFailure(accountId, response.request.url.toString())
}
return response
}

override fun finish() {
Expand Down
6 changes: 5 additions & 1 deletion auth/src/main/java/com/alfresco/auth/Credentials.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ data class Credentials(
/**
* String representation of authentication type.
*/
val authType: String
val authType: String,

val hostName: String = "",

val clientId: String = ""
)
50 changes: 47 additions & 3 deletions auth/src/main/java/com/alfresco/auth/DiscoveryService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.alfresco.auth

import android.content.Context
import android.net.Uri
import com.alfresco.auth.data.AppConfigDetails
import com.alfresco.auth.data.ContentServerDetails
import com.alfresco.auth.data.ContentServerDetailsData
import com.alfresco.auth.pkce.PkceAuthService
Expand All @@ -24,6 +25,20 @@ class DiscoveryService(
* Determine which [AuthType] is supported by the [endpoint].
*/
suspend fun getAuthType(endpoint: String): AuthType {

when (authConfig.authType.lowercase()) {
AuthType.OIDC.value -> {
if (isOIDC(endpoint)){
return AuthType.OIDC
}
}
AuthType.PKCE.value -> {
if (isPkceType(endpoint)){
return AuthType.PKCE
}
}
}

return when {

isPkceType(endpoint) -> AuthType.PKCE
Expand Down Expand Up @@ -64,6 +79,31 @@ class DiscoveryService(
}
}

suspend fun isOIDCInstalled(appConfigURL: String): Boolean {
val uri = PkceAuthService.discoveryUriWithAuth0(appConfigURL).toString()
return withContext(Dispatchers.IO) {
try {
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(URL(uri))
.get()
.build()
val response = client.newCall(request).execute()

if (response.code != 200) return@withContext false

val body = response.body?.string() ?: ""
val data = AppConfigDetails.jsonDeserialize(body)
return@withContext data?.oauth2?.audience?.isNotBlank() == true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}

/**
* returns content server details based on [endpoint].
*/
Expand Down Expand Up @@ -105,9 +145,7 @@ class DiscoveryService(
return result != null
}

private fun isOIDC(endpoint: String): Boolean {
return authConfig.realm.isBlank()
}
private suspend fun isOIDC(endpoint: String): Boolean = isOIDCInstalled(endpoint) && authConfig.realm.isBlank()

/**
* Return content service url based on [endpoint].
Expand All @@ -118,6 +156,12 @@ class DiscoveryService(
.appendPath(authConfig.contentServicePath)
.build()

fun oidcUrl(endpoint: String): Uri =
PkceAuthService.endpointWith(endpoint, authConfig)
.buildUpon()
.appendPath("alfresco")
.build()

private fun contentServiceDiscoveryUrl(endpoint: String): Uri =
contentServiceUrl(endpoint)
.buildUpon()
Expand Down
74 changes: 37 additions & 37 deletions auth/src/main/java/com/alfresco/auth/pkce/PkceAuthService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import com.alfresco.auth.R
import com.alfresco.auth.data.AppConfigDetails
import com.alfresco.auth.data.OAuth2Data
import com.alfresco.auth.ui.AuthenticationActivity
import com.alfresco.auth.ui.EndSessionActivity
import com.auth0.android.Auth0
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback
import com.auth0.android.jwt.JWT
import com.auth0.android.provider.WebAuthProvider
import com.auth0.android.result.Credentials
Expand Down Expand Up @@ -134,21 +134,19 @@ internal class PkceAuthService(context: Context, authState: AuthState?, authConf
println("PkceAuthService.initiateLogin $authDetails")

val credentials = webAuthAsync(
authConfig,
oauth2,
activity
)

withContext(Dispatchers.Main) {
(activity as AuthenticationActivity<*>).handleResult(
credentials,
authConfig
authDetails
)
}
}

}
// discoveryUriWithAuth0()
} else {
val discoveryUri = discoveryUriWith(endpoint, authConfig)

Expand All @@ -168,52 +166,52 @@ internal class PkceAuthService(context: Context, authState: AuthState?, authConf
}
}

private suspend fun loginWithBrowser(
account: Auth0,
authConfig: AuthConfig,
endpoint: String,
activity: Activity
) {
// Setup the WebAuthProvider, using the custom scheme and scope.
WebAuthProvider.login(account)
.withScheme(authConfig.scheme)
.withScope("openid profile email read:current_user update:current_user_metadata")
.withAudience("https://${endpoint}/api/v2/")

// Launch the authentication passing the callback where the results will be received
.start(activity, object : Callback<Credentials, AuthenticationException> {
override fun onFailure(exception: AuthenticationException) {
Log.d("Test OIDC 3 :: ", "Failure: ${exception.getCode()}")
}

override fun onSuccess(credentials: Credentials) {
Log.d("Test OIDC 4 :: ", "Success: ${credentials.accessToken}")
}
})
}

private suspend fun webAuthAsync(
authConfig: AuthConfig,
oauth2: OAuth2Data,
activity: Activity
): Credentials? {
return try {
val account = Auth0(oauth2.clientId, oauth2.host)
val host = URL(oauth2.host).host
Log.d("Test OIDC -1 :: ", oauth2.clientId)
Log.d("Test OIDC 0 :: ", host)
Log.d("Test OIDC 1 :: ", activity.getString(R.string.com_auth0_scheme))
Log.d("Test OIDC 2 :: ", oauth2.audience)
Log.d("Test OIDC 3 :: ", oauth2.scope)

val account = Auth0(oauth2.clientId, host)
val credentials = WebAuthProvider.login(account)
.withScheme(authConfig.scheme)
.withTrustedWebActivity()
.withScheme(activity.getString(R.string.com_auth0_scheme))
.withScope(oauth2.scope)
.withAudience(oauth2.audience)
.withScope("openid profile email")
.await(activity)
Log.d("Test OIDC 4 :: ", "Success: ${credentials.accessToken}")
credentials
} catch (error: AuthenticationException) {
val message =
if (error.isCanceled) "Browser was closed" else error.getDescription()
Log.d("Test OIDC 3 :: ", "Failure: $message")
Log.d("Test OIDC 5 :: ", "Failure: $message")
null
}
}


suspend fun logoutAuth0(hostName: String, clientId: String, activity: Activity, requestCode: Int) {
withContext(Dispatchers.IO) {

val account = Auth0(clientId, hostName)
WebAuthProvider.logout(account)
.withScheme(activity.getString(R.string.com_auth0_scheme))
.await(activity)

withContext(Dispatchers.Main) {
(activity as EndSessionActivity<*>).handleResult(requestCode)
}


}
}

fun initiateReLogin(activity: Activity, requestCode: Int) {
requireNotNull(authState.get())

Expand Down Expand Up @@ -370,10 +368,12 @@ internal class PkceAuthService(context: Context, authState: AuthState?, authConf
* @throws [IllegalArgumentException]
*/
private fun checkConfig(authConfig: AuthConfig) {
require(authConfig.contentServicePath.isNotBlank()) { "Content service path is blank or empty" }
// require(authConfig.realm.isNotBlank()) { "Realm is blank or empty" }
require(authConfig.clientId.isNotBlank()) { "Client id is blank or empty" }
require(authConfig.redirectUrl.isNotBlank()) { "Redirect url is blank or empty" }
if (authConfig.scheme.isBlank()) {
require(authConfig.contentServicePath.isNotBlank()) { "Content service path is blank or empty" }
require(authConfig.realm.isNotBlank()) { "Realm is blank or empty" }
require(authConfig.clientId.isNotBlank()) { "Client id is blank or empty" }
require(authConfig.redirectUrl.isNotBlank()) { "Redirect url is blank or empty" }
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.alfresco.auth.Credentials
import com.alfresco.auth.DiscoveryService
import com.alfresco.auth.data.LiveEvent
import com.alfresco.auth.data.MutableLiveEvent
import com.alfresco.auth.data.OAuth2Data
import com.alfresco.auth.pkce.PkceAuthService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -45,9 +46,7 @@ abstract class AuthenticationViewModel : ViewModel() {
* Check which [AuthType] is supported by the [endpoint] based on the provided [authConfig].
*/
fun checkAuthType(
endpoint: String,
authConfig: AuthConfig,
onResult: (authType: AuthType) -> Unit
endpoint: String, authConfig: AuthConfig, onResult: (authType: AuthType) -> Unit
) = viewModelScope.launch {
discoveryService = DiscoveryService(context, authConfig)
val authType = withContext(Dispatchers.IO) { discoveryService.getAuthType(endpoint) }
Expand Down Expand Up @@ -129,12 +128,12 @@ abstract class AuthenticationViewModel : ViewModel() {
}
}

fun handleActivityResult(credentials: com.auth0.android.result.Credentials) {
fun handleActivityResult(credentials: com.auth0.android.result.Credentials, oauth2: OAuth2Data) {
viewModelScope.launch {
try {
val result = credentials.accessToken
val userEmail = credentials.user.email ?: ""
_onCredentials.value = Credentials(userEmail, result, AuthType.PKCE.value)
_onCredentials.value = Credentials(userEmail, result, AuthType.OIDC.value, oauth2.host, oauth2.clientId)
} catch (ex: Exception) {
_onError.value = ex.message ?: ""
}
Expand Down Expand Up @@ -179,8 +178,8 @@ abstract class AuthenticationActivity<T : AuthenticationViewModel> : AppCompatAc
super.onActivityResult(requestCode, resultCode, data)
}

fun handleResult(data: com.auth0.android.result.Credentials?, authConfig: AuthConfig) {
data?.let { viewModel.pkceAuth.handleActivityResult(it) }
fun handleResult(data: com.auth0.android.result.Credentials?, oauth2: OAuth2Data) {
data?.let { viewModel.pkceAuth.handleActivityResult(it, oauth2) }
}

/**
Expand Down
30 changes: 23 additions & 7 deletions auth/src/main/java/com/alfresco/auth/ui/EndSessionActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.alfresco.auth.pkce.PkceAuthService
import kotlinx.coroutines.launch
import net.openid.appauth.AuthState
import org.json.JSONException
import java.net.URL

/**
* Companion [ViewModel] to [EndSessionActivity] for invoking the logout procedure.
Expand All @@ -20,7 +21,9 @@ open class EndSessionViewModel(
context: Context,
authType: AuthType?,
authState: String,
authConfig: AuthConfig
authConfig: AuthConfig,
val hostName: String,
val clientId: String
) : ViewModel() {
private val authType = authType
private val authService: PkceAuthService?
Expand All @@ -32,7 +35,7 @@ open class EndSessionViewModel(
null
}

authService = if (authType == AuthType.PKCE) {
authService = if (authType == AuthType.PKCE || authType == AuthType.OIDC) {
PkceAuthService(context, state, authConfig)
} else {
null
Expand All @@ -44,11 +47,17 @@ open class EndSessionViewModel(
*/
fun logout(activity: Activity, requestCode: Int) {
viewModelScope.launch {
if (authType == AuthType.PKCE) {
authService?.endSession(activity, requestCode)
} else {
activity.setResult(Activity.RESULT_OK)
activity.finish()
when (authType) {
AuthType.PKCE -> {
authService?.endSession(activity, requestCode)
}
AuthType.OIDC -> {
authService?.logoutAuth0(URL(hostName).host,clientId, activity, requestCode)
}
else -> {
activity.setResult(Activity.RESULT_OK)
activity.finish()
}
}
}
}
Expand Down Expand Up @@ -82,6 +91,13 @@ abstract class EndSessionActivity<out T : EndSessionViewModel> : AppCompatActivi
}
}

fun handleResult(requestCode: Int) {
if (requestCode == REQUEST_CODE_END_SESSION) {
setResult(Activity.RESULT_OK)
finish()
}
}

private companion object {
const val REQUEST_CODE_END_SESSION = 1
}
Expand Down
Loading

0 comments on commit e3845c1

Please sign in to comment.