Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public open class AuthenticationActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
WebAuthProvider.onRestoreInstanceState(savedInstanceState)
WebAuthProvider.onRestoreInstanceState(savedInstanceState, this)
intentLaunched = savedInstanceState.getBoolean(EXTRA_INTENT_LAUNCHED, false)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.auth0.android.provider

import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback

/**
* Wraps a user-provided callback and observes the Activity/Fragment lifecycle.
* When the host is destroyed (e.g. config change), [delegateCallback] is set to null so
* the destroyed Activity is no longer referenced by the SDK.
*
* If a result arrives after [delegateCallback] has been cleared, the [onDetached] lambda
* is invoked to cache the result for later recovery via resumePending*Result().
*
* @param S the success type (Credentials for login, Void? for logout)
* @param delegateCallback the user's original callback
* @param lifecycleOwner the Activity or Fragment whose lifecycle to observe
* @param onDetached called when a result arrives but the callback is already detached
*/
internal class LifecycleAwareCallback<S>(
@Volatile private var delegateCallback: Callback<S, AuthenticationException>?,
lifecycleOwner: LifecycleOwner,
private val onDetached: (success: S?, error: AuthenticationException?) -> Unit,
) : Callback<S, AuthenticationException>, DefaultLifecycleObserver {

init {
lifecycleOwner.lifecycle.addObserver(this)
}

override fun onSuccess(result: S) {
val cb = delegateCallback
if (cb != null) {
cb.onSuccess(result)
} else {
onDetached(result, null)
}
}

override fun onFailure(error: AuthenticationException) {
val cb = delegateCallback
if (cb != null) {
cb.onFailure(error)
} else {
onDetached(null, error)
}
}

override fun onDestroy(owner: LifecycleOwner) {
delegateCallback = null
owner.lifecycle.removeObserver(this)
}
}
15 changes: 12 additions & 3 deletions auth0/src/main/java/com/auth0/android/provider/OAuthManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal class OAuthManager(
@get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal val dPoP: DPoP? = null
) : ResumableManager() {

private val parameters: MutableMap<String, String>
private val headers: MutableMap<String, String>
private val ctOptions: CustomTabsOptions
Expand Down Expand Up @@ -211,7 +212,8 @@ internal class OAuthManager(
auth0 = account,
idTokenVerificationIssuer = idTokenVerificationIssuer,
idTokenVerificationLeeway = idTokenVerificationLeeway,
customAuthorizeUrl = this.customAuthorizeUrl
customAuthorizeUrl = this.customAuthorizeUrl,
dPoPEnabled = dPoP != null
)
}

Expand Down Expand Up @@ -387,14 +389,21 @@ internal class OAuthManager(

internal fun OAuthManager.Companion.fromState(
state: OAuthManagerState,
callback: Callback<Credentials, AuthenticationException>
callback: Callback<Credentials, AuthenticationException>,
context: Context
): OAuthManager {
// Enable DPoP on the restored PKCE's AuthenticationAPIClient so that
// the token exchange request includes the DPoP proof after process restore.
if (state.dPoPEnabled && state.pkce != null) {
state.pkce.apiClient.useDPoP(context)
}
return OAuthManager(
account = state.auth0,
ctOptions = state.ctOptions,
parameters = state.parameters,
callback = callback,
customAuthorizeUrl = state.customAuthorizeUrl
customAuthorizeUrl = state.customAuthorizeUrl,
dPoP = if (state.dPoPEnabled) DPoP(context) else null
).apply {
setHeaders(
state.headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.util.Base64
import androidx.core.os.ParcelCompat
import com.auth0.android.Auth0
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.dpop.DPoP
import com.auth0.android.request.internal.GsonProvider
import com.google.gson.Gson

Expand All @@ -20,7 +19,7 @@ internal data class OAuthManagerState(
val idTokenVerificationLeeway: Int?,
val idTokenVerificationIssuer: String?,
val customAuthorizeUrl: String? = null,
val dPoP: DPoP? = null
val dPoPEnabled: Boolean = false
) {

private class OAuthManagerJson(
Expand All @@ -37,7 +36,7 @@ internal data class OAuthManagerState(
val idTokenVerificationLeeway: Int?,
val idTokenVerificationIssuer: String?,
val customAuthorizeUrl: String? = null,
val dPoP: DPoP? = null
val dPoPEnabled: Boolean = false
)

fun serializeToJson(
Expand All @@ -62,7 +61,7 @@ internal data class OAuthManagerState(
idTokenVerificationIssuer = idTokenVerificationIssuer,
idTokenVerificationLeeway = idTokenVerificationLeeway,
customAuthorizeUrl = this.customAuthorizeUrl,
dPoP = this.dPoP
dPoPEnabled = this.dPoPEnabled
)
return gson.toJson(json)
} finally {
Expand Down Expand Up @@ -112,7 +111,7 @@ internal data class OAuthManagerState(
idTokenVerificationIssuer = oauthManagerJson.idTokenVerificationIssuer,
idTokenVerificationLeeway = oauthManagerJson.idTokenVerificationLeeway,
customAuthorizeUrl = oauthManagerJson.customAuthorizeUrl,
dPoP = oauthManagerJson.dPoP
dPoPEnabled = oauthManagerJson.dPoPEnabled
)
} finally {
parcel.recycle()
Expand Down
Loading
Loading