Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
26 changes: 13 additions & 13 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ WebAuthProvider.login(account)
> [!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 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.
[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()` method.

```kotlin
WebAuthProvider
.useDPoP(requireContext())
.useDPoP()
.login(account)
.start(requireContext(), object : Callback<Credentials, AuthenticationException> {
override fun onSuccess(result: Credentials) {
Expand All @@ -241,7 +241,7 @@ When making requests to your own APIs, use the `DPoP.getHeaderData()` method to
```kotlin
val url ="https://example.com/api/endpoint"
val httpMethod = "GET"
val headerData = DPoPProvider.getHeaderData(
val headerData = DPoP.getHeaderData(
httpMethod, url,
accessToken, tokenType
)
Expand All @@ -252,10 +252,10 @@ httpRequest.apply{
}
}
```
If your API is issuing DPoP nonces to prevent replay attacks, you can pass the nonce value to the `getHeaderData()` method to include it in the DPoP proof. Use the `DPoPProvider.isNonceRequiredError(response: Response)` method to check if a particular API response failed because a nonce is required.
If your API is issuing DPoP nonces to prevent replay attacks, you can pass the nonce value to the `getHeaderData()` method to include it in the DPoP proof. Use the `DPoP.isNonceRequiredError(response: Response)` method to check if a particular API response failed because a nonce is required.

```kotlin
if (DPoPProvider.isNonceRequiredError(response)) {
if (DPoP.isNonceRequiredError(response)) {
val nonce = response.headers["DPoP-Nonce"]
val dpopProof = DPoPProvider.generateProof(
url, httpMethod, accessToken, nonce
Expand All @@ -264,7 +264,7 @@ if (DPoPProvider.isNonceRequiredError(response)) {
}
```

On logout, you should call `DPoPProvider.clearKeyPair()` to delete the user's key pair from the Keychain.
On logout, you should call `DPoP.clearKeyPair()` to delete the user's key pair from the Keychain.

```kotlin
WebAuthProvider.logout(account)
Expand Down Expand Up @@ -728,10 +728,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(context: Context)` 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 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.

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

[!IMPORTANT]
Expand All @@ -744,7 +744,7 @@ When making requests to your own APIs, use the `DPoP.getHeaderData()` method to
```kotlin
val url ="https://example.com/api/endpoint"
val httpMethod = "GET"
val headerData = DPoPProvider.getHeaderData(
val headerData = DPoP.getHeaderData(
httpMethod, url,
accessToken, tokenType
)
Expand All @@ -755,10 +755,10 @@ httpRequest.apply{
}
}
```
If your API is issuing DPoP nonces to prevent replay attacks, you can pass the nonce value to the `getHeaderData()` method to include it in the DPoP proof. Use the `DPoPProvider.isNonceRequiredError(response: Response)` method to check if a particular API response failed because a nonce is required.
If your API is issuing DPoP nonces to prevent replay attacks, you can pass the nonce value to the `getHeaderData()` method to include it in the DPoP proof. Use the `DPoP.isNonceRequiredError(response: Response)` method to check if a particular API response failed because a nonce is required.

```kotlin
if (DPoPProvider.isNonceRequiredError(response)) {
if (DPoP.isNonceRequiredError(response)) {
val nonce = response.headers["DPoP-Nonce"]
val dpopProof = DPoPProvider.generateProof(
url, httpMethod, accessToken, nonce
Expand All @@ -767,11 +767,11 @@ if (DPoPProvider.isNonceRequiredError(response)) {
}
```

On logout, you should call `DPoPProvider.clearKeyPair()` to delete the user's key pair from the Keychain.
On logout, you should call `DPoP.clearKeyPair()` to delete the user's key pair from the Keychain.

```kotlin

DPoPProvider.clearKeyPair()
DPoP.clearKeyPair()

```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package com.auth0.android.authentication

import android.content.Context
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import com.auth0.android.Auth0
import com.auth0.android.Auth0Exception
import com.auth0.android.NetworkErrorException
import com.auth0.android.dpop.DPoP
import com.auth0.android.dpop.DPoPException
import com.auth0.android.dpop.DPoPProvider
import com.auth0.android.dpop.SenderConstraining
import com.auth0.android.request.*
import com.auth0.android.request.internal.*
Expand Down Expand Up @@ -44,6 +40,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
private val gson: Gson
) : SenderConstraining<AuthenticationAPIClient> {

private var dPoP: DPoP? = null

/**
* Creates a new API client instance providing Auth0 account info.
*
Expand All @@ -66,13 +64,11 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
public val baseURL: String
get() = auth0.getDomainUrl()


/**
* Enable DPoP for this client.
*/
@RequiresApi(Build.VERSION_CODES.M)
public override fun useDPoP(context: Context): AuthenticationAPIClient {
DPoPProvider.generateKeyPair(context)
public override fun useDPoP(): AuthenticationAPIClient {
dPoP = DPoP()
return this
}

Expand Down Expand Up @@ -579,25 +575,10 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
* @return a request to start
*/
public fun userInfo(
accessToken: String, tokenType: String
accessToken: String, tokenType: String = "Bearer"
): Request<UserProfile, AuthenticationException> {
return profileRequest().apply {
try {
val headerData = DPoPProvider.getHeaderData(
getHttpMethod().toString(),
getUrl(),
accessToken,
tokenType,
DPoPProvider.auth0Nonce
)
addHeader(HEADER_AUTHORIZATION, headerData.authorizationHeader)
headerData.dpopProof?.let {
addHeader(DPoPProvider.DPOP_HEADER, it)
}
} catch (exception: DPoPException) {
Log.e(TAG, "Error generating DPoP proof: ${exception.stackTraceToString()}")
}
}
return profileRequest()
.addHeader(HEADER_AUTHORIZATION, "$tokenType $accessToken")
}

/**
Expand Down Expand Up @@ -824,9 +805,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
val credentialsAdapter = GsonAdapter(
Credentials::class.java, gson
)
val request = factory.post(url.toString(), credentialsAdapter)
val request = factory.post(url.toString(), credentialsAdapter, dPoP)
.addParameters(parameters)
.addDPoPHeader()
return request
}

Expand Down Expand Up @@ -962,9 +942,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
val credentialsAdapter: JsonAdapter<Credentials> = GsonAdapter(
Credentials::class.java, gson
)
val request = factory.post(url.toString(), credentialsAdapter)
val request = factory.post(url.toString(), credentialsAdapter, dPoP)
.addParameters(parameters)
.addDPoPHeader()
return request
}

Expand Down Expand Up @@ -1029,9 +1008,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
val adapter: JsonAdapter<T> = GsonAdapter(
T::class.java, gson
)
val request = factory.post(url.toString(), adapter)
val request = factory.post(url.toString(), adapter, dPoP)
.addParameters(requestParameters)
.addDPoPHeader()
return request
}

Expand All @@ -1052,10 +1030,9 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
Credentials::class.java, gson
)
val request = BaseAuthenticationRequest(
factory.post(url.toString(), credentialsAdapter), clientId, baseURL
factory.post(url.toString(), credentialsAdapter, dPoP), clientId, baseURL
)
request.addParameters(requestParameters)
.addDPoPHeader()
return request
}

Expand All @@ -1082,21 +1059,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
val userProfileAdapter: JsonAdapter<UserProfile> = GsonAdapter(
UserProfile::class.java, gson
)
return factory.get(url.toString(), userProfileAdapter)
}

/**
* Helper method to add DPoP proof to all the [Request]
*/
private fun <T> Request<T, AuthenticationException>.addDPoPHeader(): Request<T, AuthenticationException> {
try {
DPoPProvider.generateProof(getUrl(), getHttpMethod().toString())?.let {
addHeader(DPoPProvider.DPOP_HEADER, it)
}
} catch (exception: DPoPException) {
Log.e(TAG, "Error generating DPoP proof: ${exception.stackTraceToString()}")
}
return this
return factory.get(url.toString(), userProfileAdapter, dPoP)
}

private companion object {
Expand Down Expand Up @@ -1163,6 +1126,11 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
"Failed to execute the network request", NetworkErrorException(cause)
)
}
if (cause is DPoPException) {
return AuthenticationException(
cause.message ?: "Error while attaching DPoP proof", cause
)
}
return AuthenticationException(
"Something went wrong", Auth0Exception("Something went wrong", cause)
)
Expand Down
Loading