Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2cf6765
feat: Upgrade Auth0.swift to v3.0.2 (#909)
NandanPrabhu Aug 5, 2026
3d68c9f
feat: Upgrade Auth0.Android to v4.0.0. (#918)
utkrishtsahu Aug 7, 2026
902a942
Chore/sync develop/v3.0 with main branch (#926)
utkrishtsahu Aug 21, 2026
acdc736
feat: honor useEphemeralSession on Android web auth (#927)
utkrishtsahu Aug 25, 2026
335effe
fix: register Android web auth callbacks with the activity lifecycle …
utkrishtsahu Aug 25, 2026
2aeeef8
feat: add credentialsManager.clearAll() for full credential and key w…
utkrishtsahu Aug 25, 2026
16adcc6
feat: default credentialsManager minTtl to 60 seconds (#929)
utkrishtsahu Aug 25, 2026
8bf4d34
feat: change SSOCredentials.expiresIn to expiresAt (Android + iOS) (#…
utkrishtsahu Aug 25, 2026
a182c94
feat: reconcile WebAuthenticationException error codes across Android…
utkrishtsahu Sep 4, 2026
085a6c9
feat: dpop web auth init config (#938)
utkrishtsahu Sep 4, 2026
8dd8e67
feat: remove inline MFA methods from the Authentication API. (#943)
utkrishtsahu Sep 8, 2026
c07c26e
feat: add nonce to IdTokenValidationConfig for Web Auth login (#944)
utkrishtsahu Sep 9, 2026
7b4fa73
chore: sync main into develop/v3.0 (#945)
NandanPrabhu Sep 9, 2026
bf0391c
Sync/main to develop v3 (#951)
utkrishtsahu Sep 15, 2026
dc1ccd7
docs: restructure v3 migration guide and update README for v3 require…
utkrishtsahu Sep 16, 2026
acd5cc3
Release afpi-v3.0.0-beta.0 (#954)
utkrishtsahu Sep 16, 2026
55275b3
Release af-v3.0.0-beta.0 (#955)
utkrishtsahu Sep 16, 2026
c48d1ec
feat(android): add opt-in Trusted Web Activity and Auth Tab support (…
utkrishtsahu Oct 13, 2026
c9c9e40
build(deps): bump Auth0.Android to 4.2.0, Auth0.swift to 3.2.0 and au…
utkrishtsahu Oct 23, 2026
fdeda7d
chore: sync main into develop/v3.0
utkrishtsahu Oct 26, 2026
8a2891c
Chore/sync main into develop v3 (#976)
utkrishtsahu Oct 26, 2026
844ff05
fix(android): bump Auth0.Android to 4.2.1 to keep RSA key for PKCS1 m…
utkrishtsahu Oct 26, 2026
a088ebd
Fix/bump auth0 android 4.2.1 (#977)
utkrishtsahu Oct 27, 2026
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
4 changes: 2 additions & 2 deletions .github/actions/setup-darwin/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ runs:
echo "::error::FlutterGeneratedPluginSwiftPackage/Package.swift not found at expected path"
exit 1
fi
sed -i '' 's/\.iOS(".*")/.iOS("14.0")/g' "$PACKAGE_FILE"
sed -i '' 's/\.iOS(".*")/.iOS("15.0")/g' "$PACKAGE_FILE"
shell: bash

- name: Fix macOS minimum deployment target for SPM
Expand All @@ -90,7 +90,7 @@ runs:
echo "::error::FlutterGeneratedPluginSwiftPackage/Package.swift not found at expected path"
exit 1
fi
sed -i '' 's/\.macOS(".*")/.macOS("11.0")/g' "$PACKAGE_FILE"
sed -i '' 's/\.macOS(".*")/.macOS("12.0")/g' "$PACKAGE_FILE"
shell: bash

- name: Set .env
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Apply these on every task in this repo — they keep changes correct, small, and
- **Language:** Dart 3.5.0+ (Flutter 3.24.0+), plus native Kotlin (Android), Swift (iOS/macOS via SPM/CocoaPods), and C++ (Windows)
- **Tech Stack:** Federated Flutter plugin — a Dart-facing package (`auth0_flutter`) and a platform-interface package (`auth0_flutter_platform_interface`) that dispatches to native implementations over `MethodChannel`; web uses `dart:js_interop` over `auth0-spa-js`
- **Package Manager:** `pub` (Dart/Flutter); native deps via CocoaPods/SPM (Apple), Gradle (Android), vcpkg (Windows)
- **Minimum Platform Version:** Android API 21+, iOS 14+, macOS 11+, Windows 10+
- **Minimum Platform Version:** Android API 26+, iOS 15+, macOS 12+, Windows 10+
- **Dependencies:** `auth0_flutter_platform_interface`, `plugin_platform_interface`, `web` · native: SimpleKeychain 1.3.0 (Apple) · test: `mockito`, `flutter_test`, `dart_jsonwebtoken` — see `auth0_flutter/pubspec.yaml` and `auth0_flutter_platform_interface/pubspec.yaml` for the full list

---
Expand Down
22 changes: 22 additions & 0 deletions auth0_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,28 @@
- fix: bump Auth0.Android to 3.21.2 for biometric failure fix [\#968](https://github.com/auth0/auth0-flutter/pull/968) ([utkrishtsahu](https://github.com/utkrishtsahu))
- fix(android): bump Auth0.Android to 3.21.1 for Keystore2 INCOMPATIBLE_DEVICE fix [\#963](https://github.com/auth0/auth0-flutter/pull/963) ([utkrishtsahu](https://github.com/utkrishtsahu))

## [af-v3.0.0-beta.0](https://github.com/auth0/auth0-flutter/tree/af-v3.0.0-beta.0) (2026-09-16)
[Full Changelog](https://github.com/auth0/auth0-flutter/compare/af-v2.6.2...af-v3.0.0-beta.0)

**Breaking Change**
- feat: remove inline MFA methods from the Authentication API. [\#943](https://github.com/auth0/auth0-flutter/pull/943) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: reconcile WebAuthenticationException error codes across Android and iOS [\#935](https://github.com/auth0/auth0-flutter/pull/935) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: change SSOCredentials.expiresIn to expiresAt (Android + iOS) [\#931](https://github.com/auth0/auth0-flutter/pull/931) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: Upgrade Auth0.Android to v4.0.0. [\#918](https://github.com/auth0/auth0-flutter/pull/918) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: Upgrade Auth0.swift to v3.0.2 [\#909](https://github.com/auth0/auth0-flutter/pull/909) ([NandanPrabhu](https://github.com/NandanPrabhu))

**Added**
- feat: add nonce to IdTokenValidationConfig for Web Auth login [\#944](https://github.com/auth0/auth0-flutter/pull/944) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: dpop web auth init config [\#938](https://github.com/auth0/auth0-flutter/pull/938) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: add credentialsManager.clearAll() for full credential and key wipe [\#930](https://github.com/auth0/auth0-flutter/pull/930) ([utkrishtsahu](https://github.com/utkrishtsahu))

**Changed**
- feat: default credentialsManager minTtl to 60 seconds [\#929](https://github.com/auth0/auth0-flutter/pull/929) ([utkrishtsahu](https://github.com/utkrishtsahu))
- feat: honor useEphemeralSession on Android web auth [\#927](https://github.com/auth0/auth0-flutter/pull/927) ([utkrishtsahu](https://github.com/utkrishtsahu))

**Fixed**
- fix: register Android web auth callbacks with the activity lifecycle [\#928](https://github.com/auth0/auth0-flutter/pull/928) ([utkrishtsahu](https://github.com/utkrishtsahu))

## [af-v2.6.2](https://github.com/auth0/auth0-flutter/tree/af-v2.6.2) (2026-09-15)
[Full Changelog](https://github.com/auth0/auth0-flutter/compare/af-v2.6.1...af-v2.6.2)

Expand Down
85 changes: 79 additions & 6 deletions auth0_flutter/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [1. Configure a custom URL scheme](#1-configure-a-custom-url-scheme)
- [2. Capture the callback URL](#2-capture-the-callback-url)
- [Using Partial Custom Tabs (Android only)](#using-partial-custom-tabs-android-only)
- [Using Trusted Web Activity or Auth Tab (Android only)](#using-trusted-web-activity-or-auth-tab-android-only)
- [Errors](#errors)
- [Android: Custom schemes](#android-custom-schemes)
- [🪟 Windows Web Authentication](#-windows-web-authentication)
Expand Down Expand Up @@ -262,6 +263,16 @@ final credentials =
await auth0.webAuthentication().login(idTokenValidationConfig: config);
```

You can also supply your own `nonce` to be validated against the `nonce` claim of the returned ID token. When omitted, the SDK generates and validates one automatically.

```dart
const config = IdTokenValidationConfig(nonce: 'a1b2c3d4');
final credentials =
await auth0.webAuthentication().login(idTokenValidationConfig: config);
```

> ℹ️ `nonce` is not supported on Web, where it is always managed internally by auth0-spa-js.

</details>

<details>
Expand Down Expand Up @@ -395,6 +406,8 @@ The available options are:
| `sideSheetBreakpoint` | `int?` | Screen width threshold (dp) to switch between bottom sheet and side sheet. Defaults to the browser's built-in value (typically 840dp). |
| `backgroundInteractionEnabled` | `bool?` | Whether the user can interact with the app behind the partial tab. Defaults to `false`. |
| `allowedBrowsers` | `List<String>` | Allowlist of browser packages for Custom Tabs. |
| `useTrustedWebActivity` | `bool` | Launch the browser as a [Trusted Web Activity](#using-trusted-web-activity-or-auth-tab-android-only). Defaults to `false`. |
| `useAuthTab` | `bool` | Launch the browser as an [Auth Tab](#using-trusted-web-activity-or-auth-tab-android-only). Defaults to `false`. |

You can also use `customTabsOptions` during logout:

Expand All @@ -406,6 +419,35 @@ await auth0.webAuthentication().logout(
));
```

### Using Trusted Web Activity or Auth Tab (Android only)

Both launch mechanisms are opt-in and are enabled through `CustomTabsOptions`. They apply to `login` and `logout`, and are ignored on iOS, macOS, Windows and web.

- **Auth Tab** uses the Chrome Custom Tabs Activity Result API instead of inferring the result from the activity lifecycle. This avoids spurious `isUserCancelledException` errors when Chrome is minimized. It requires Chrome 137+; older browsers fall back to a standard Custom Tab.
- **Trusted Web Activity (TWA)** opens the Auth0 domain without browser UI. Your app must be verified for the domain through [Digital Asset Links](https://developer.android.com/training/app-links/verify-android-applinks).

```dart
// Auth Tab
await auth0.webAuthentication().login(
customTabsOptions: const CustomTabsOptions(useAuthTab: true));

// Trusted Web Activity
await auth0.webAuthentication().logout(
customTabsOptions: const CustomTabsOptions(useTrustedWebActivity: true));
```

The two are mutually exclusive. If both are set to `true`, Trusted Web Activity takes precedence and Auth Tab is not used.

#### Ephemeral sessions

`useEphemeralSession: true` on `login` is honored by Auth Tab: the tab runs in an isolated session when the browser supports it (Chrome 136+ or a compatible browser), and otherwise falls back to a regular Auth Tab. It has no effect with Trusted Web Activity, because a TWA shares the user's browser profile by design and cannot run in an isolated session.

```dart
await auth0.webAuthentication().login(
useEphemeralSession: true,
customTabsOptions: const CustomTabsOptions(useAuthTab: true));
```

### Errors

<details>
Expand All @@ -418,15 +460,21 @@ try {
final credentials = await auth0.webAuthentication().login();
// ...
} on WebAuthenticationException catch (e) {
if (e.isRetryable) {
if (e.isUserCancelledException) {
// User dismissed the browser — same on Android and iOS
} else if (e.isRetryable) {
// Transient error (e.g. network issue) — safe to retry
} else if (e.code == 'dpop_jkt_mismatch') {
// DPoP thumbprint mismatch returned by the Auth0 server (Android + iOS)
} else {
print(e);
}
}
```

The `isRetryable` property indicates whether the error is transient (e.g. a network outage) and the operation can be retried.
- `isUserCancelledException` — `true` when the user dismisses the browser without completing login (Android + iOS).
- `isRetryable` — `true` when the error is transient (e.g. a network outage) and the operation can be retried.
- For server-returned errors (e.g. `dpop_jkt_mismatch`, `access_denied`), `exception.code` contains the raw server error code directly.

</details>

Expand Down Expand Up @@ -710,6 +758,13 @@ final credentials = await auth0.credentialsManager.credentials();

> 💡 You do not need to call `credentialsManager.storeCredentials()` afterward. The Credentials Manager automatically persists the renewed credentials.

> 💡 By default, `credentials()` (and `getApiCredentials()`) requires the access token to have at least **60 seconds** of remaining lifetime, matching Auth0.Android v4 and Auth0.swift v3. If the token would expire sooner, it is refreshed using the refresh token. Pass `minTtl: 0` to return the stored token regardless of how soon it expires, or a larger value to require more headroom:
>
> ```dart
> // Return the stored token even if it is about to expire.
> final credentials = await auth0.credentialsManager.credentials(minTtl: 0);
> ```

### 🕒 Session expiry from an upstream IdP

> ⚠️ **Early Access:** upstream IdP session expiry (the IPSIE [`session_expiry`](https://openid.github.io/ipsie-openid-sl1/draft-openid-ipsie-sl1-profile.html) claim) is an **Early Access** feature. The API surface described here (the `sessionExpiry` field and the `isSessionExpired` error) may change before it is generally available.
Expand Down Expand Up @@ -792,6 +847,22 @@ await auth0.credentialsManager.clearApiCredentials(
>
> 💡 Stored API credentials are keyed by **both** audience and scope on every platform, so pass the same `scope` to `clearApiCredentials()` that you used when fetching them. The native APIs do not consistently report whether a matching entry existed, so this method returns `void` rather than a success flag.

### Clear all credentials and encryption keys

To wipe **everything** the Credentials Manager stores – all credentials, all cached API credentials, and the underlying encryption keys – use `clearAll()`:

```dart
await auth0.credentialsManager.clearAll();
```

> 💡 `clearAll()` is a more thorough wipe than `clearCredentials()` or `clearApiCredentials()`:
>
> - `clearCredentials()` removes the stored credential entries only. On iOS/macOS it deletes the credentials, DPoP thumbprint, and session-expiry entries; on Android it clears the credentials store.
> - `clearApiCredentials(audience:)` removes only the cached API credentials for a specific audience (and scope), leaving the main credentials and the encryption keys intact.
> - `clearAll()` additionally removes the cryptographic keys used to protect the stored data – on Android the crypto key pair and the DPoP key; on iOS/macOS every entry in the credentials store plus the DPoP key pair.
>
> ⚠️ Because `clearAll()` deletes *all* entries in the underlying store, avoid sharing the Credentials Manager's storage (for example, a custom `sharedPreferencesName` on Android or `storeKey`/`accessGroup` on iOS) with unrelated app data.

### Retrieve user profile

Fetch the user profile associated with the stored credentials. This method returns `null` if no credentials are present in storage.
Expand Down Expand Up @@ -933,7 +1004,7 @@ try {

print('Session Transfer Token: ${ssoCredentials.sessionTransferToken}');
print('Token Type: ${ssoCredentials.tokenType}');
print('Expires In: ${ssoCredentials.expiresIn} seconds');
print('Expires At: ${ssoCredentials.expiresAt}');
} on CredentialsManagerException catch (e) {
print('Failed to get SSO credentials: ${e.message}');
}
Expand All @@ -946,7 +1017,7 @@ try {
final ssoCredentials = await auth0.api.ssoExchange(refreshToken: refreshToken);

print('Session Transfer Token: ${ssoCredentials.sessionTransferToken}');
print('Expires In: ${ssoCredentials.expiresIn} seconds');
print('Expires At: ${ssoCredentials.expiresAt}');
} on ApiException catch (e) {
print('SSO Exchange failed: ${e.code} - ${e.message}');
}
Expand Down Expand Up @@ -1513,7 +1584,7 @@ final credentials = await auth0.passwordless.loginWithOtp(
</details>

> [!NOTE]
> If the user has MFA configured, `loginWithOtp` fails with an [`ApiException`](#errors-2) whose `isMultifactorRequired` is `true`. Continue the flow using the existing MFA APIs (`auth0.api.multifactorChallenge` / `auth0.api.loginWithOtp`).
> If the user has MFA configured, `loginWithOtp` fails with an [`ApiException`](#errors-2) whose `isMultifactorRequired` is `true`. Continue the flow using the MFA APIs (`auth0.mfa.challenge` / `auth0.mfa.verifyOtp`).

To receive DPoP-bound tokens from the token exchange when DPoP is enabled for your client, construct your `Auth0` instance with `useDPoP: true`:

Expand Down Expand Up @@ -1753,7 +1824,7 @@ try {
if (e.isVerificationRequired) {
final credentials = await auth0.webAuthentication().login(
scopes: scopes,
useEphemeralSession: true, // Otherwise a session cookie will remain (iOS/macOS only)
useEphemeralSession: true, // Otherwise a session cookie will remain (see note below)
parameters: {
'connection': connection,
'login_hint': email // So the user doesn't have to type it again
Expand All @@ -1763,6 +1834,8 @@ try {
}
```

> 💡 `useEphemeralSession` starts a private browser session so no session cookie is persisted. It is honored on iOS/macOS and, as of v3, on Android. On Android it depends on the device browser supporting ephemeral (private) Custom Tabs sessions — if the browser doesn't support it, the login falls back to a normal Custom Tabs session and is **not** ephemeral.

---

[Go up ⤴](#examples)
Expand Down
10 changes: 5 additions & 5 deletions auth0_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

## What's New

> 📖 **Migrating from auth0_flutter v2?** See the [v3 Migration Guide](V3_MIGRATION_GUIDE.md) for the raised platform requirements, changed defaults, and breaking changes.

**auth0_flutter** supports **Passkeys** on Android, iOS, and Web. On mobile, use `Auth0.api.passkeyLoginChallenge`/`passkeySignupChallenge`/`passkeyCredentialExchange`; on web, use the equivalent `Auth0Web.passkeyLoginChallenge`/`passkeySignupChallenge`/`getTokenByPasskey` (requires `auth0-spa-js` v2.24.0+). See the [Auth0 passkeys documentation](https://auth0.com/docs/authenticate/database-connections/passkeys) for setup instructions.

## Documentation
Expand All @@ -28,8 +30,8 @@

| Flutter | Android | iOS | macOS | Windows |
| :---------- | :-------------- | :---------------- | :---------------- | :------------------------------- |
| SDK 3.24.0+ | Android API 21+ | iOS 14+ | macOS 11+ | Windows 10+ |
| Dart 3.5.0+ | Java 8+ | Swift 5.9+ | Swift 5.9+ | C++ 17, Visual Studio 2022 |
| SDK 3.24.0+ | Android API 26+ | iOS 15+ | macOS 12+ | Windows 10+ |
| Dart 3.5.0+ | Java 17+ | Swift 5.9+ | Swift 5.9+ | C++ 17, Visual Studio 2022 |
| | | Xcode 15.x / 16.x | Xcode 15.x / 16.x | vcpkg (for dependencies) |

### Installation
Expand Down Expand Up @@ -368,7 +370,7 @@ final auth0Web = Auth0Web('YOUR_AUTH0_DOMAIN', 'YOUR_AUTH0_CLIENT_ID');
Finally, in your `index.html` add the following `<script>` tag:

```html
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.24/auth0-spa-js.production.js" defer></script>
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.29/auth0-spa-js.production.js" defer></script>
```

> ⚠️ If you use [Custom Token Exchange](EXAMPLES.md#custom-token-exchange) with delegation/impersonation (an actor token), you must reference `auth0-spa-js` **2.20.0 or later**. Actor-token support was added to the underlying SPA SDK in [2.20.0](https://github.com/auth0/auth0-spa-js/releases/tag/v2.20.0); older versions silently drop the actor token, so the returned ID token has no `act` claim and `credentials.user.actor` is `null`.
Expand Down Expand Up @@ -627,8 +629,6 @@ void dispose() {
#### API

- [login](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/login.html)
- [loginWithOtp](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/loginWithOtp.html)
- [multifactorChallenge](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/multifactorChallenge.html)
- [renewCredentials](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/renewCredentials.html)
- [resetPassword](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/resetPassword.html)
- [signup](https://pub.dev/documentation/auth0_flutter/latest/auth0_flutter/AuthenticationApi/signup.html)
Expand Down
Loading
Loading