-
Notifications
You must be signed in to change notification settings - Fork 231
feat: add support for Demonstration of Proof-of-Possession(DPoP) #1345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…oP key management
…n across platforms
…into feat/dpop-support
android/build.gradle
Outdated
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" | ||
implementation "androidx.browser:browser:1.2.0" | ||
implementation 'com.auth0.android:auth0:3.8.0' | ||
implementation 'com.auth0.android:auth0:3.9.1' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use 3.10.0 just so that it is the latest Android SDK
private const val DPOP_KEY_RETRIEVAL_FAILED_CODE = "DPOP_KEY_RETRIEVAL_FAILED" | ||
private const val DPOP_KEYSTORE_ERROR_CODE = "DPOP_KEYSTORE_ERROR" | ||
private const val DPOP_CRYPTO_ERROR_CODE = "DPOP_CRYPTO_ERROR" | ||
private const val DPOP_GENERATION_FAILED_CODE = "DPOP_GENERATION_FAILED" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DPOP_GENERATION_FAILED_CODE do you need a specific code for this. DPoP generation will always fail due to one of the other codes added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its an generic error for both android and iOS in case of anything unexpected happen in DPoP flow.
Let me know if you think I should remove it completely
} | ||
|
||
@ReactMethod | ||
override fun getDPoPHeaders(url: String, method: String, accessToken: String, tokenType: String, promise: Promise) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also take an optional nonce parameter for the flows which would require a nonce value
|
||
@ReactMethod | ||
@DoNotStrip | ||
abstract fun getDPoPHeaders(url: String, method: String, accessToken: String, tokenType: String, promise: Promise) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add optional nonce parameter
ios/NativeBridge.swift
Outdated
resolve(removed) | ||
} | ||
|
||
@objc public func getDPoPHeaders(url: String, method: String, accessToken: String, tokenType: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would also require an optional nonce parameter
ios/NativeBridge.swift
Outdated
} | ||
|
||
// Validate URL format | ||
guard let urlObj = URL(string: url) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably combine this check into a single one with the initial check of !url.isEmpty
/** The access token to bind to the request. */ | ||
accessToken: string; | ||
/** The type of the token (should be 'DPoP' when DPoP is enabled). */ | ||
tokenType: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an optional nonce parameter to this contract
…te auth0 dependency to 3.10.0
…ate URL format in NativeBridge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements DPoP (Demonstrating Proof-of-Possession) RFC 9449 support across iOS, Android, and Web platforms, adding cryptographic binding for OAuth 2.0 tokens to enhance security.
Key Changes:
- Upgraded native SDK dependencies (Auth0.swift 2.13.0→2.14.0, Auth0.Android 3.8.0→3.10.0, auth0-spa-js 2.3.0→2.7.0)
- Added
getDPoPHeaders()
API method anduseAuth0().getDPoPHeaders()
hook for custom API requests - Introduced
useDPoP
configuration option (default: true) and newDPoPError
class with normalized error codes - Integrated DPoP key cleanup into logout flow via
clearCredentials()
Reviewed Changes
Copilot reviewed 26 out of 29 changed files in this pull request and generated 3 comments.
Show a summary per file
File | Description |
---|---|
src/types/common.ts | Added useDPoP option and DPoPHeadersParams interface |
src/specs/NativeA0Auth0.ts | Added useDPoP parameter and DPoP method signatures to Turbo Module spec |
src/platforms/web/adapters/WebAuth0Client.ts | Implemented web DPoP header generation using auth0-spa-js |
src/platforms/native/adapters/NativeAuth0Client.ts | Implemented native DPoP header generation with error wrapping |
src/platforms/native/bridge/* | Added bridge methods for getDPoPHeaders and clearDPoPKey |
src/core/models/DPoPError.ts | New error class with cross-platform error code normalization |
src/hooks/* | Added getDPoPHeaders to Auth0Context and Auth0Provider |
ios/NativeBridge.swift | iOS DPoP implementation with Secure Enclave/Keychain integration |
android/.../A0Auth0Module.kt | Android DPoP implementation with KeyStore integration |
package.json | Updated auth0-spa-js dependency to 2.7.0 |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
DPoPException.MALFORMED_URL -> DPOP_MISSING_PARAMETER_CODE | ||
DPoPException.UNSUPPORTED_ERROR -> DPOP_ERROR_CODE | ||
DPoPException.UNKNOWN_ERROR -> DPOP_GENERATION_FAILED_CODE | ||
else -> DPOP_GENERATION_FAILED_CODE |
Copilot
AI
Oct 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This when expression matches DPoPException instances as if they were enum values, but the code suggests DPoPException is likely a sealed class or enum. Without exhaustive matching (removing the 'else' branch), new DPoPException types added in future SDK versions could silently fall through to the default case. Consider making this exhaustive or adding a comment explaining why the else branch is needed.
else -> DPOP_GENERATION_FAILED_CODE |
Copilot uses AI. Check for mistakes.
case .secKeyOperationFailed: code = NativeBridge.dpopProofFailedCode | ||
case .other: code = NativeBridge.dpopErrorCode | ||
case .unknown: code = NativeBridge.dpopErrorCode | ||
default: |
Copilot
AI
Oct 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The switch statement on DPoPError uses a 'default' case, which may mask new error types added in future versions of Auth0.swift. Consider making this switch exhaustive by removing the default and handling all DPoPError cases explicitly, or add a @unknown default if Swift version supports it.
default: | |
@unknown default: |
Copilot uses AI. Check for mistakes.
Changes
This PR implements DPoP (RFC 9449) support across all platforms (iOS, Android, and Web) for react-native-auth0, enabling sender-constrained OAuth 2.0 tokens for enhanced security.
SDK Version Updates
New Public API Methods
1.
Auth0.getDPoPHeaders(params: DPoPHeadersParams): Promise<Record<string, string>>
url
,method
,accessToken
,tokenType
Authorization
andDPoP
headers2.
useAuth0().getDPoPHeaders(params: DPoPHeadersParams)
New Configuration Options
Auth0Options.useDPoP?: boolean
(default:true
)New Error Types
DPoPError
class extendingAuthError
with normalized error codes:DPOP_GENERATION_FAILED
- General DPoP generation failureDPOP_PROOF_FAILED
- DPoP proof generation failureDPOP_KEY_GENERATION_FAILED
- Key pair generation failureDPOP_KEY_STORAGE_FAILED
- Key storage failureDPOP_KEY_RETRIEVAL_FAILED
- Key retrieval failureDPOP_NONCE_MISMATCH
- Nonce validation failureDPOP_INVALID_TOKEN_TYPE
- Invalid token typeDPOP_MISSING_PARAMETER
- Required parameter missingDPOP_CLEAR_KEY_FAILED
- Key cleanup failureNative Bridge Methods Added
iOS (NativeBridge.swift):
getDPoPHeaders(url:method:accessToken:tokenType:resolve:reject:)
clearDPoPKey(resolve:reject:)
DPoPError.reactNativeErrorCode()
- Maps DPoP errors to RN error codesAndroid (A0Auth0Module.kt):
getDPoPHeaders(url:method:accessToken:tokenType:promise)
clearDPoPKey(promise)
handleDPoPError(error:promise)
- Private error handler with exact exception matchingWeb (WebAuth0Client.ts):
getDPoPHeaders(params)
- Uses auth0-spa-js internal DPoP utilitiesModified Methods
INativeBridge.initialize()
useDPoP?: boolean
parametertrue
ICredentialsManager.clearCredentials()
Architecture Overview
Usage Example
React Hook Usage
References
Testing
Unit Tests
Platform Testing
iOS:
Android:
Web:
Integration Tests
Not Tested
DPoP with Refresh Token Exchange: Per Auth0.swift documentation, DPoP is not applied to existing refresh token exchanges. This is expected behavior and a limitation of the current DPoP implementation in the native SDKs.
Checklist
DPoPError
classBreaking Changes
None. This is a backward-compatible feature addition.
useDPoP: true
), but existing applications will continue to workMigration Notes
For applications that want to adopt DPoP:
useDPoP: true
credentials.tokenType === 'DPoP'
before callinggetDPoPHeaders()
getDPoPHeaders()
for custom API requests when using DPoP tokensSecurity Considerations
✅ Key Storage: All platforms use secure storage
✅ Key Cleanup: DPoP keys are automatically cleared on logout
✅ Error Security: Error messages do not leak sensitive cryptographic details
✅ Token Binding: DPoP cryptographically binds tokens to device-specific keys, preventing token theft attacks