Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fb1e954
Adding DPoP feature for flutter
utkrishtsahu Oct 6, 2025
59561ef
feat(android): Upgrade native SDK to 3.9.0 and adapt to breaking changes
utkrishtsahu Oct 14, 2025
f36ce25
adding Dp feature with platform updates
utkrishtsahu Nov 20, 2025
dcc06db
adding changes as per github action bot
utkrishtsahu Nov 20, 2025
9f037ee
addressed bot PR review comments
utkrishtsahu Nov 20, 2025
f97a18f
Merge main branch to bring DPoP branch up to date
utkrishtsahu Nov 20, 2025
284b62f
Fixing UT failure issue in CI for android,ios and macos
utkrishtsahu Nov 20, 2025
df70633
Fixed review comments by prince and claude
utkrishtsahu Nov 21, 2025
bcf3a5a
restored to java 8 and handled review comments
utkrishtsahu Nov 27, 2025
ceb6c24
Resolving Claud code review
utkrishtsahu Dec 1, 2025
5b47793
Handle review comments and added all UT for flutter including DPoP
utkrishtsahu Dec 2, 2025
8d7cc7e
addressing unit test failure for android,ios and symlink
utkrishtsahu Dec 2, 2025
866b1fb
Fix race conditions in iOS/macOS unit tests and document cache-only b…
utkrishtsahu Dec 3, 2025
3b7e369
Fix iOS/macOS unit test failures
utkrishtsahu Dec 3, 2025
36ebe7a
Resilving review comments and ios UT test failure
utkrishtsahu Dec 3, 2025
c317750
Merge remote-tracking branch 'origin/main' into DPoP_upport_flutter
utkrishtsahu Dec 3, 2025
e3e6cb6
Fix CI failures: Add missing files and update test mocks
utkrishtsahu Dec 3, 2025
accc43b
Fix iOS/macOS unit test timeouts by reverting async test changes
utkrishtsahu Dec 3, 2025
a5f483d
Fix iOS unit test timeouts by adding explicit 5s timeouts to all expe…
utkrishtsahu Dec 3, 2025
a9d50cd
fix iOS unit test timeouts by reverting to proven async/sync patterns
utkrishtsahu Dec 5, 2025
131f157
Fixes iOS UTs
NandanPrabhu Dec 5, 2025
0e65bbf
Adds scenedelegate conformance
NandanPrabhu Dec 5, 2025
2d46380
Address review comments: separate DPoP channel and simplify docs
utkrishtsahu Dec 5, 2025
c3926f2
Fix test: use correct index for CredentialsManagerMethodCallHandler
utkrishtsahu Dec 5, 2025
a29e2fd
Removed UT as per review comments
utkrishtsahu Dec 5, 2025
8740337
addresed few review comments
utkrishtsahu Dec 5, 2025
e41aade
refactor: Move DPoP utility methods from AuthenticationApi to Auth0 c…
utkrishtsahu Dec 5, 2025
6071b97
Handled review comments:Decoupling in all platforms,removal of commen…
utkrishtsahu Dec 8, 2025
5b8471d
Merge remote-tracking branch 'origin/main' into DPoP_upport_flutter
utkrishtsahu Dec 8, 2025
16055d4
added UT in CredentialsManagerHandlerTests.swift
utkrishtsahu Dec 8, 2025
fbfb893
feat: refactor DPoP operations and add tokenType parameter to userPro…
utkrishtsahu Dec 9, 2025
9cdf344
Fix iOS CredentialsManager caching tests
utkrishtsahu Dec 9, 2025
6c12e93
Fixing ios CI failure
utkrishtsahu Dec 9, 2025
2ef754e
Handling review comments UserInfoApiRequestHandler and AuthAPIUserInf…
utkrishtsahu Dec 9, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:
ruby: '3.3.1'
flutter: '3.x'
ios-simulator: iPhone 16
java: 11
java: 17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this needs to be upgraded ?


jobs:

Expand Down
31 changes: 27 additions & 4 deletions auth0_flutter/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,16 @@ final auth0 = Auth0('YOUR_AUTH0_DOMAIN', 'YOUR_AUTH0_CLIENT_ID',

You can enable an additional level of user authentication before retrieving credentials using the local authentication supported by the device, for example PIN or fingerprint on Android, and Face ID or Touch ID on iOS.

To enable this, pass a `LocalAuthentication` instance when you create your `Auth0` object.

```dart
const localAuthentication =
LocalAuthentication(title: 'Please authenticate to continue');
final auth0 = Auth0('YOUR_AUTH0_DOMAIN', 'YOUR_AUTH0_CLIENT_ID',
localAuthentication: localAuthentication);
final credentials = await auth0.credentialsManager.credentials();
```
> ⚠️ On Android, your app's `MainActivity.kt` file must extend `FlutterFragmentActivity` instead of `FlutterActivity` for biometric prompts to work.

Check the [API documentation](https://pub.dev/documentation/auth0_flutter_platform_interface/latest/auth0_flutter_platform_interface/LocalAuthentication-class.html) to learn more about the available `LocalAuthentication` properties.

Expand Down Expand Up @@ -490,10 +493,16 @@ The Credentials Manager will only throw `CredentialsManagerException` exceptions

```dart
try {
final credentials = await auth0.credentialsManager.credentials();
// ...
final credentials = await auth0.credentialsManager.credentials();
// ...
} on CredentialsManagerException catch (e) {
print(e);
if (e.isNoCredentialsFound) {
print("No credentials stored.");
} else if (e.isTokenRenewFailed) {
print("Failed to renew tokens.");
} else {
print(e);
}
}
```

Expand Down Expand Up @@ -659,9 +668,23 @@ Fetch the latest user information from the `/userinfo` endpoint.
This method will yield a `UserProfile` instance. Check the [API documentation](https://pub.dev/documentation/auth0_flutter_platform_interface/latest/auth0_flutter_platform_interface/UserProfile-class.html) to learn more about its available properties.

```dart
final userProfile = await auth0.api.userInfo(accessToken: accessToken);
// Basic usage with Bearer token (default)
final userProfile = await auth0.api.userProfile(accessToken: accessToken);

// With explicit token type (useful for DPoP tokens)
final credentials = await auth0.credentialsManager.credentials();
final userProfile = await auth0.api.userProfile(
accessToken: credentials.accessToken,
tokenType: credentials.tokenType, // 'Bearer' or 'DPoP'
);
```

The `tokenType` parameter specifies the type of token being used:
- `'Bearer'` (default): Standard OAuth 2.0 bearer tokens
- `'DPoP'`: DPoP (Demonstrating Proof of Possession) tokens for enhanced security

> πŸ’‘ When using DPoP tokens, the SDK automatically handles proof generation. See the [DPoP documentation](DPOP.md) for more information.

### Renew credentials

Use a [refresh token](https://auth0.com/docs/secure/tokens/refresh-tokens) to renew the user's credentials. It's recommended that you read and understand the refresh token process beforehand.
Expand Down
84 changes: 84 additions & 0 deletions auth0_flutter/MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Migration Guide

## Native SDK Version Updates

This release includes updates to the underlying native Auth0 SDKs to support new features including DPoP (Demonstrating Proof of Possession). These updates are **transparent** to your application code - no code changes are required unless you want to opt into new features like DPoP.

### Updated SDK Versions

| Platform | Previous Version | New Version | Changes |
|----------|-----------------|-------------|---------|
| **Android** | Auth0.Android 2.11.0 | Auth0.Android 3.11.0 | DPoP support, **biometric auth requires FlutterFragmentActivity** |
| **iOS/macOS** | Auth0.swift 2.10.0 | Auth0.swift 2.14.0 | DPoP support, improved APIs |
| **Web** | auth0-spa-js 2.0 | auth0-spa-js 2.9.0 | DPoP support, bug fixes |

### What's New

#### DPoP (Demonstrating Proof of Possession) Support
All platforms now support DPoP, an optional OAuth 2.0 security extension that cryptographically binds access tokens to your client, preventing token theft and replay attacks.

**This is an opt-in feature** - your existing authentication flows will continue to work without any changes.

To enable DPoP:
```dart
// Mobile
final credentials = await auth0.webAuthentication().login(useDPoP: true);

// Web
final auth0Web = Auth0Web('DOMAIN', 'CLIENT_ID', useDPoP: true);
```

For complete DPoP documentation, see the [README](README.md#using-dpop-demonstrating-proof-of-possession).

### Do I Need to Make Changes?

**Most users do not need to make changes.** However, there is one breaking change that affects users of biometric authentication on Android.

#### ⚠️ Breaking Change: Android Biometric Authentication

**If you use biometric authentication on Android**, your `MainActivity.kt` must now extend `FlutterFragmentActivity` instead of `FlutterActivity`.

This requirement comes from Auth0.Android SDK 3.x, which changed its biometric authentication implementation.

**Who is affected:**
- βœ… Users who call `credentialsManager.credentials()` with `localAuthentication` parameter
- βœ… Only on Android platform
- βœ… Only if your `MainActivity.kt` currently extends `FlutterActivity`

**Required change:**

```kotlin
// Before (will cause error)
import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}

// After (required for biometric auth)
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterFragmentActivity() {
}
```

**If you don't use biometric authentication,** no changes are needed.

#### Optional New Features

You only need to make changes if you want to:
- βœ… Enable DPoP for enhanced security (optional)
- βœ… Use new DPoP API methods: `getDPoPHeaders()` and `clearDPoPKey()` (optional)

### Java Version Requirement (Android)

**Java 8** remains the minimum requirement for Android builds. The SDK continues to use:
- `sourceCompatibility JavaVersion.VERSION_1_8`
- `targetCompatibility JavaVersion.VERSION_1_8`

No changes to your Java setup are needed.

## What's New

This version includes support for **DPoP (Demonstrating Proof of Possession)**, an optional OAuth 2.0 security feature that cryptographically binds access tokens to a specific client. DPoP is completely opt-in and your existing authentication flows will continue to work without any modifications.

For detailed DPoP usage instructions, see the [README DPoP section](README.md#using-dpop-demonstrating-proof-of-possession).
42 changes: 42 additions & 0 deletions auth0_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ Re-declare the activity manually using `tools:node="remove"` in the `android/src

> πŸ’‘ If your Android app is using [product flavors](https://developer.android.com/studio/build/build-variants#product-flavors), you might need to specify different manifest placeholders for each flavor.

##### Android: Biometric authentication

> ⚠️ On Android, your app's `MainActivity.kt` file must extend `FlutterFragmentActivity` instead of `FlutterActivity` for biometric prompts to work.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be considered a breaking change for customer who currently uses biometric authentication ?


##### iOS/macOS: Configure the associated domain

> ⚠️ This step requires a paid Apple Developer account. It is needed to use Universal Links as callback and logout URLs.
Expand Down Expand Up @@ -343,6 +347,44 @@ final credentials = await auth0Web.loginWithPopup(popupWindow: popup);

For other comprehensive examples, see the [EXAMPLES.md](EXAMPLES.md) document.

### Using DPoP (Demonstrating Proof of Possession)

Auth0 Flutter SDK supports [DPoP (Demonstrating Proof of Possession)](https://datatracker.ietf.org/doc/html/rfc9449), a security mechanism that cryptographically binds access tokens to your client, preventing token theft and replay attacks.

**Quick Start:**

```dart
// Mobile (Android/iOS)
final credentials = await auth0
.webAuthentication()
.login(useDPoP: true, useHTTPS: true);

// Web
final auth0Web = Auth0Web(
'YOUR_AUTH0_DOMAIN',
'YOUR_AUTH0_CLIENT_ID',
useDPoP: true,
);
```

**Key Benefits:**
- πŸ”’ Enhanced security through cryptographic token binding
- πŸ›‘οΈ Protection against token theft and replay attacks
- 🌐 Full cross-platform support (Web, Android, iOS)

**Platform Support:**

| Feature | Web | iOS | Android |
|---------|-----|-----|---------|
| Login with DPoP | βœ… | βœ… | βœ… |
| CredentialsManager with DPoP | βœ… | βœ… | βœ… |
| Token Refresh with DPoP | βœ… | βœ… | βœ… |
| Manual DPoP APIs (`getDPoPHeaders()`, `clearDPoPKey()`) | βœ… | βœ… | βœ… |

> **Note:** In most cases, DPoP is managed automatically when `useDPoP: true` is enabled. Manual DPoP APIs are available for advanced use cases where you need direct control over DPoP proof generation.

πŸ“– **For complete DPoP documentation, examples, and troubleshooting, see [DPOP.md](DPOP.md)**

### iOS SSO Alert Box

![Screenshot of the SSO alert box](https://user-images.githubusercontent.com/5055789/198689762-8f3459a7-fdde-4c14-a13b-68933ef675e6.png)
Expand Down
15 changes: 8 additions & 7 deletions auth0_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ android {
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
test.resources.srcDirs += 'src/test/resources'
}

defaultConfig {
Expand All @@ -71,15 +72,15 @@ android {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//noinspection GradleDynamicVersion
implementation 'com.auth0.android:auth0:2.11.0'

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.auth0.android:auth0:3.11.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0'
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.1.0"
testImplementation "org.mockito:mockito-inline:4.11.0"
testImplementation 'com.jayway.awaitility:awaitility:1.7.0'
testImplementation 'org.robolectric:robolectric:4.6.1'
testImplementation 'org.robolectric:robolectric:4.11.1'
testImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
testImplementation 'com.auth0:java-jwt:3.19.1'
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package com.auth0.auth0_flutter

import android.content.Context
import androidx.annotation.NonNull
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.auth0_flutter.request_handlers.api.*
import com.auth0.auth0_flutter.request_handlers.api.ApiRequestHandler
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result


class Auth0FlutterAuthMethodCallHandler(private val requestHandlers: List<ApiRequestHandler>) : MethodCallHandler {
class Auth0FlutterAuthMethodCallHandler(
private val apiRequestHandlers: List<ApiRequestHandler>
) : MethodCallHandler {
lateinit var context: Context

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
val requestHandler = requestHandlers.find { it.method == call.method }

if (requestHandler != null) {
val request = MethodCallRequest.fromCall(call)
val request = MethodCallRequest.fromCall(call)
val apiHandler = apiRequestHandlers.find { it.method == call.method }
if (apiHandler != null) {
val api = AuthenticationAPIClient(request.account)

val useDPoP = request.data["useDPoP"] as? Boolean ?: false
if (useDPoP) {
api.useDPoP(context)
}

requestHandler.handle(api, request, result)
apiHandler.handle(api, request, result)
} else {
result.notImplemented()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.auth0.auth0_flutter

import androidx.annotation.NonNull
import com.auth0.auth0_flutter.request_handlers.api.UtilityRequestHandler
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/**
* Handler for DPoP-related method calls.
* DPoP (Demonstration of Proof-of-Possession) operations use static utility methods
* and don't require authentication API client instances.
*/
class Auth0FlutterDPoPMethodCallHandler(
private val dpopRequestHandlers: List<UtilityRequestHandler>
) : MethodCallHandler {

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
val request = MethodCallRequest.fromCall(call)

val dpopHandler = dpopRequestHandlers.find { it.method == call.method }

if (dpopHandler != null) {
dpopHandler.handle(request, result)
} else {
result.notImplemented()
}
}
}
Loading
Loading