Skip to content
Merged
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
80 changes: 78 additions & 2 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,10 +738,10 @@ authentication
> [!NOTE]
> This feature is currently available in [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access). Please reach out to Auth0 support to get it enabled for your tenant.

[DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) (Demonstrating Proof of Posession) is an application-level mechanism for sender-constraining OAuth 2.0 access and refresh tokens by proving that the app is in possession of a certain private key. You can enable it by calling the `useDPoP()` method. This ensures that DPoP proofs are generated for requests made through the AuthenticationAPI client.
[DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) (Demonstrating Proof of Possession) is an application-level mechanism for sender-constraining OAuth 2.0 access and refresh tokens by proving that the app is in possession of a certain private key. You can enable it by calling the `useDPoP(context: Context)` method. This ensures that DPoP proofs are generated for requests made through the AuthenticationAPI client.

```kotlin
val client = AuthenticationAPIClient(account).useDPoP()
val client = AuthenticationAPIClient(account).useDPoP(context)
```

[!IMPORTANT]
Expand Down Expand Up @@ -785,6 +785,17 @@ DPoP.clearKeyPair()

```

To use DPoP with `SecureCredentialsManager` you need to pass an instance of the `AuthenticationAPIClient` with DPoP enabled to the `SecureCredentialsManager` constructor.

```kotlin
val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
val apiClient = AuthenticationAPIClient(auth0).useDPoP(this)
val storage = SharedPreferencesStorage(this)
val manager = SecureCredentialsManager(apiClient, this, auth0, storage)

```


> [!NOTE]
> DPoP is supported only on Android version 6.0 (API level 23) and above. Trying to use DPoP in any older versions will result in an exception.

Expand Down Expand Up @@ -1382,6 +1393,28 @@ SecureCredentialsManager manager = new SecureCredentialsManager(this, account, s
```
</details>

#### Using a Custom AuthenticationAPIClient

If you need to configure the `AuthenticationAPIClient` with advanced features (such as DPoP), you can pass your own configured instance to `SecureCredentialsManager`:

```kotlin
val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
val apiClient = AuthenticationAPIClient(auth0).useDPoP(this)
val storage = SharedPreferencesStorage(this)
val manager = SecureCredentialsManager(apiClient, this, auth0, storage)
```

<details>
<summary>Using Java</summary>

```java
Auth0 auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN");
AuthenticationAPIClient apiClient = new AuthenticationAPIClient(auth0).useDPoP(this);
Storage storage = new SharedPreferencesStorage(this);
SecureCredentialsManager manager = new SecureCredentialsManager(apiClient, this, auth0, storage);
```
</details>

#### Requiring Authentication

You can require the user authentication to obtain credentials. This will make the manager prompt the user with the device's configured Lock Screen, which they must pass correctly in order to obtain the credentials. **This feature is only available on devices where the user has setup a secured Lock Screen** (PIN, Pattern, Password or Fingerprint).
Expand Down Expand Up @@ -1419,6 +1452,49 @@ SecureCredentialsManager secureCredentialsManager = new SecureCredentialsManager
```
</details>

You can also combine biometric authentication with a custom `AuthenticationAPIClient`:

```kotlin
val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
val apiClient = AuthenticationAPIClient(auth0).useDPoP(this)
val localAuthenticationOptions =
LocalAuthenticationOptions.Builder()
.setTitle("Authenticate")
.setDescription("Accessing Credentials")
.setAuthenticationLevel(AuthenticationLevel.STRONG)
.setNegativeButtonText("Cancel")
.setDeviceCredentialFallback(true)
.setPolicy(BiometricPolicy.Session(300))
.build()
val storage = SharedPreferencesStorage(this)
val manager = SecureCredentialsManager(
apiClient, this, auth0, storage, fragmentActivity,
localAuthenticationOptions
)
```

<details>
<summary>Using Java</summary>

```java
Auth0 auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN");
AuthenticationAPIClient apiClient = new AuthenticationAPIClient(auth0).useDPoP(this);
LocalAuthenticationOptions localAuthenticationOptions =
new LocalAuthenticationOptions.Builder()
.setTitle("Authenticate")
.setDescription("Accessing Credentials")
.setAuthenticationLevel(AuthenticationLevel.STRONG)
.setNegativeButtonText("Cancel")
.setDeviceCredentialFallback(true)
.setPolicy(new BiometricPolicy.Session(300))
.build();
Storage storage = new SharedPreferencesStorage(this);
SecureCredentialsManager secureCredentialsManager = new SecureCredentialsManager(
apiClient, this, auth0, storage, fragmentActivity,
localAuthenticationOptions);
```
</details>

**Points to be Noted**:

On Android API 29 and below, specifying **DEVICE_CREDENTIAL** alone as the authentication level is not supported.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
package com.auth0.android.authentication.storage

import android.text.TextUtils
import android.util.Base64
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.authentication.storage.SecureCredentialsManager.Companion.KEY_CREDENTIALS
import com.auth0.android.callback.Callback
import com.auth0.android.request.internal.GsonProvider
import com.auth0.android.request.internal.Jwt
import com.auth0.android.result.APICredentials
import com.auth0.android.result.Credentials
import com.auth0.android.result.OptionalCredentials
import com.auth0.android.result.SSOCredentials
import com.auth0.android.result.UserProfile
import com.auth0.android.result.toAPICredentials
import com.google.gson.Gson
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.*
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,19 @@ import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback
import com.auth0.android.request.internal.GsonProvider
import com.auth0.android.request.internal.Jwt
import com.auth0.android.result.APICredentials
import com.auth0.android.result.Credentials
import com.auth0.android.result.OptionalCredentials
import com.auth0.android.result.SSOCredentials
import com.auth0.android.result.UserProfile
import com.auth0.android.result.toAPICredentials
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import java.lang.ref.WeakReference
import java.util.*
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicLong
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

Expand Down Expand Up @@ -65,6 +60,34 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
storage: Storage,
) : this(
AuthenticationAPIClient(auth0),
context,
auth0,
storage
)

/**
* Creates a new SecureCredentialsManager to handle Credentials with a custom AuthenticationAPIClient instance.
* Use this constructor when you need to configure the API client with advanced features like DPoP.
*
* Example usage:
* ```
* val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
* val apiClient = AuthenticationAPIClient(auth0).useDPoP(context)
* val manager = SecureCredentialsManager(apiClient, context, auth0, storage)
* ```
*
* @param apiClient a configured AuthenticationAPIClient instance
* @param context a valid context
* @param auth0 the Auth0 account information to use
* @param storage the storage implementation to use
*/
public constructor(
apiClient: AuthenticationAPIClient,
context: Context,
auth0: Auth0,
storage: Storage
) : this(
apiClient,
storage,
CryptoUtil(context, storage, KEY_ALIAS),
JWTDecoder(),
Expand All @@ -89,6 +112,50 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
localAuthenticationOptions: LocalAuthenticationOptions
) : this(
AuthenticationAPIClient(auth0),
context,
auth0,
storage,
fragmentActivity,
localAuthenticationOptions
)


/**
* Creates a new SecureCredentialsManager to handle Credentials with biometrics Authentication
* and a custom AuthenticationAPIClient instance.
* Use this constructor when you need to configure the API client with advanced features like DPoP
* along with biometric authentication.
*
* Example usage:
* ```
* val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
* val apiClient = AuthenticationAPIClient(auth0).useDPoP(context)
* val manager = SecureCredentialsManager(
* apiClient,
* context,
* auth0,
* storage,
* fragmentActivity,
* localAuthenticationOptions
* )
* ```
*
* @param apiClient a configured AuthenticationAPIClient instance
* @param context a valid context
* @param auth0 the Auth0 account information to use
* @param storage the storage implementation to use
* @param fragmentActivity the FragmentActivity to use for the biometric authentication
* @param localAuthenticationOptions the options of type [LocalAuthenticationOptions] to use for the biometric authentication
*/
public constructor(
apiClient: AuthenticationAPIClient,
context: Context,
auth0: Auth0,
storage: Storage,
fragmentActivity: FragmentActivity,
localAuthenticationOptions: LocalAuthenticationOptions
) : this(
apiClient,
storage,
CryptoUtil(context, storage, KEY_ALIAS),
JWTDecoder(),
Expand Down Expand Up @@ -270,7 +337,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
if (credentials == null) {
return null
}
return credentials.user
return credentials.user
}

/**
Expand Down Expand Up @@ -1138,7 +1205,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
internal fun isBiometricSessionValid(): Boolean {
val lastAuth = lastBiometricAuthTime.get()
if (lastAuth == NO_SESSION) return false // No session exists

return when (val policy = biometricPolicy) {
is BiometricPolicy.Session,
is BiometricPolicy.AppLifecycle -> {
Expand All @@ -1149,6 +1216,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
} * 1000L
System.currentTimeMillis() - lastAuth < timeoutMillis
}

is BiometricPolicy.Always -> false
}
}
Expand Down