Skip to content

Commit e33d3d7

Browse files
committed
Review comments addressed and changed the DPoP flow a bit
1 parent 93bf422 commit e33d3d7

File tree

21 files changed

+2188
-811
lines changed

21 files changed

+2188
-811
lines changed

EXAMPLES.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,11 @@ WebAuthProvider.login(account)
215215
> [!NOTE]
216216
> 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.
217217
218-
[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.
218+
[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.
219219

220220
```kotlin
221221
WebAuthProvider
222-
.useDPoP(requireContext())
222+
.useDPoP()
223223
.login(account)
224224
.start(requireContext(), object : Callback<Credentials, AuthenticationException> {
225225
override fun onSuccess(result: Credentials) {
@@ -241,7 +241,7 @@ When making requests to your own APIs, use the `DPoP.getHeaderData()` method to
241241
```kotlin
242242
val url ="https://example.com/api/endpoint"
243243
val httpMethod = "GET"
244-
val headerData = DPoPProvider.getHeaderData(
244+
val headerData = DPoP.getHeaderData(
245245
httpMethod, url,
246246
accessToken, tokenType
247247
)
@@ -252,10 +252,10 @@ httpRequest.apply{
252252
}
253253
}
254254
```
255-
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.
255+
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.
256256

257257
```kotlin
258-
if (DPoPProvider.isNonceRequiredError(response)) {
258+
if (DPoP.isNonceRequiredError(response)) {
259259
val nonce = response.headers["DPoP-Nonce"]
260260
val dpopProof = DPoPProvider.generateProof(
261261
url, httpMethod, accessToken, nonce
@@ -264,7 +264,7 @@ if (DPoPProvider.isNonceRequiredError(response)) {
264264
}
265265
```
266266

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

269269
```kotlin
270270
WebAuthProvider.logout(account)
@@ -728,10 +728,10 @@ authentication
728728
> [!NOTE]
729729
> 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.
730730
731-
[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.
731+
[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.
732732

733733
```kotlin
734-
val client = AuthenticationAPIClient(account).useDPoP(context)
734+
val client = AuthenticationAPIClient(account).useDPoP()
735735
```
736736

737737
[!IMPORTANT]
@@ -744,7 +744,7 @@ When making requests to your own APIs, use the `DPoP.getHeaderData()` method to
744744
```kotlin
745745
val url ="https://example.com/api/endpoint"
746746
val httpMethod = "GET"
747-
val headerData = DPoPProvider.getHeaderData(
747+
val headerData = DPoP.getHeaderData(
748748
httpMethod, url,
749749
accessToken, tokenType
750750
)
@@ -755,10 +755,10 @@ httpRequest.apply{
755755
}
756756
}
757757
```
758-
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.
758+
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.
759759

760760
```kotlin
761-
if (DPoPProvider.isNonceRequiredError(response)) {
761+
if (DPoP.isNonceRequiredError(response)) {
762762
val nonce = response.headers["DPoP-Nonce"]
763763
val dpopProof = DPoPProvider.generateProof(
764764
url, httpMethod, accessToken, nonce
@@ -767,11 +767,11 @@ if (DPoPProvider.isNonceRequiredError(response)) {
767767
}
768768
```
769769

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

772772
```kotlin
773773

774-
DPoPProvider.clearKeyPair()
774+
DPoP.clearKeyPair()
775775

776776
```
777777

auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package com.auth0.android.authentication
22

3-
import android.content.Context
4-
import android.os.Build
5-
import android.util.Log
6-
import androidx.annotation.RequiresApi
73
import androidx.annotation.VisibleForTesting
84
import com.auth0.android.Auth0
95
import com.auth0.android.Auth0Exception
106
import com.auth0.android.NetworkErrorException
7+
import com.auth0.android.dpop.DPoP
118
import com.auth0.android.dpop.DPoPException
12-
import com.auth0.android.dpop.DPoPProvider
139
import com.auth0.android.dpop.SenderConstraining
1410
import com.auth0.android.request.*
1511
import com.auth0.android.request.internal.*
@@ -44,6 +40,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
4440
private val gson: Gson
4541
) : SenderConstraining<AuthenticationAPIClient> {
4642

43+
private var dPoP: DPoP? = null
44+
4745
/**
4846
* Creates a new API client instance providing Auth0 account info.
4947
*
@@ -66,13 +64,11 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
6664
public val baseURL: String
6765
get() = auth0.getDomainUrl()
6866

69-
7067
/**
7168
* Enable DPoP for this client.
7269
*/
73-
@RequiresApi(Build.VERSION_CODES.M)
74-
public override fun useDPoP(context: Context): AuthenticationAPIClient {
75-
DPoPProvider.generateKeyPair(context)
70+
public override fun useDPoP(): AuthenticationAPIClient {
71+
dPoP = DPoP()
7672
return this
7773
}
7874

@@ -579,25 +575,10 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
579575
* @return a request to start
580576
*/
581577
public fun userInfo(
582-
accessToken: String, tokenType: String
578+
accessToken: String, tokenType: String = "Bearer"
583579
): Request<UserProfile, AuthenticationException> {
584-
return profileRequest().apply {
585-
try {
586-
val headerData = DPoPProvider.getHeaderData(
587-
getHttpMethod().toString(),
588-
getUrl(),
589-
accessToken,
590-
tokenType,
591-
DPoPProvider.auth0Nonce
592-
)
593-
addHeader(HEADER_AUTHORIZATION, headerData.authorizationHeader)
594-
headerData.dpopProof?.let {
595-
addHeader(DPoPProvider.DPOP_HEADER, it)
596-
}
597-
} catch (exception: DPoPException) {
598-
Log.e(TAG, "Error generating DPoP proof: ${exception.stackTraceToString()}")
599-
}
600-
}
580+
return profileRequest()
581+
.addHeader(HEADER_AUTHORIZATION, "$tokenType $accessToken")
601582
}
602583

603584
/**
@@ -824,9 +805,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
824805
val credentialsAdapter = GsonAdapter(
825806
Credentials::class.java, gson
826807
)
827-
val request = factory.post(url.toString(), credentialsAdapter)
808+
val request = factory.post(url.toString(), credentialsAdapter, dPoP)
828809
.addParameters(parameters)
829-
.addDPoPHeader()
830810
return request
831811
}
832812

@@ -962,9 +942,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
962942
val credentialsAdapter: JsonAdapter<Credentials> = GsonAdapter(
963943
Credentials::class.java, gson
964944
)
965-
val request = factory.post(url.toString(), credentialsAdapter)
945+
val request = factory.post(url.toString(), credentialsAdapter, dPoP)
966946
.addParameters(parameters)
967-
.addDPoPHeader()
968947
return request
969948
}
970949

@@ -1029,9 +1008,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
10291008
val adapter: JsonAdapter<T> = GsonAdapter(
10301009
T::class.java, gson
10311010
)
1032-
val request = factory.post(url.toString(), adapter)
1011+
val request = factory.post(url.toString(), adapter, dPoP)
10331012
.addParameters(requestParameters)
1034-
.addDPoPHeader()
10351013
return request
10361014
}
10371015

@@ -1052,10 +1030,9 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
10521030
Credentials::class.java, gson
10531031
)
10541032
val request = BaseAuthenticationRequest(
1055-
factory.post(url.toString(), credentialsAdapter), clientId, baseURL
1033+
factory.post(url.toString(), credentialsAdapter, dPoP), clientId, baseURL
10561034
)
10571035
request.addParameters(requestParameters)
1058-
.addDPoPHeader()
10591036
return request
10601037
}
10611038

@@ -1082,21 +1059,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
10821059
val userProfileAdapter: JsonAdapter<UserProfile> = GsonAdapter(
10831060
UserProfile::class.java, gson
10841061
)
1085-
return factory.get(url.toString(), userProfileAdapter)
1086-
}
1087-
1088-
/**
1089-
* Helper method to add DPoP proof to all the [Request]
1090-
*/
1091-
private fun <T> Request<T, AuthenticationException>.addDPoPHeader(): Request<T, AuthenticationException> {
1092-
try {
1093-
DPoPProvider.generateProof(getUrl(), getHttpMethod().toString())?.let {
1094-
addHeader(DPoPProvider.DPOP_HEADER, it)
1095-
}
1096-
} catch (exception: DPoPException) {
1097-
Log.e(TAG, "Error generating DPoP proof: ${exception.stackTraceToString()}")
1098-
}
1099-
return this
1062+
return factory.get(url.toString(), userProfileAdapter, dPoP)
11001063
}
11011064

11021065
private companion object {
@@ -1163,6 +1126,11 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
11631126
"Failed to execute the network request", NetworkErrorException(cause)
11641127
)
11651128
}
1129+
if (cause is DPoPException) {
1130+
return AuthenticationException(
1131+
cause.message ?: "Error while attaching DPoP proof", cause
1132+
)
1133+
}
11661134
return AuthenticationException(
11671135
"Something went wrong", Auth0Exception("Something went wrong", cause)
11681136
)

0 commit comments

Comments
 (0)