diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index afefc719d..80086cc27 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -307,6 +307,72 @@ jobs:
name: Test results
path: 'auth0_flutter/example/build/app/reports/androidTests/*.xml'
+ test-windows-unit:
+ name: Run native Windows unit tests
+ runs-on: windows-latest
+ environment: ${{ github.event.pull_request.head.repo.fork && 'external' || 'internal' }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
+
+ - name: Install Flutter
+ uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # pin@v2.21.0
+ with:
+ flutter-version: ${{ env.flutter }}
+ channel: stable
+ cache: true
+
+ - name: Add example/.env
+ working-directory: auth0_flutter
+ run: Copy-Item example/.env.example example/.env
+ shell: powershell
+
+ - name: Set up vcpkg
+ uses: lukka/run-vcpkg@v11 # pin@v11
+ with:
+ vcpkgDirectory: '${{ github.workspace }}/vcpkg'
+ vcpkgGitCommitId: '66c0373dc7fca549e5803087b9487edfe3aca0a1'
+
+ - name: Install vcpkg dependencies
+ run: |
+ ${{ github.workspace }}\vcpkg\vcpkg install cpprestsdk:x64-windows openssl:x64-windows boost-system:x64-windows boost-date-time:x64-windows boost-regex:x64-windows
+ shell: cmd
+
+ - name: Build Windows example app
+ working-directory: auth0_flutter/example
+ run: flutter build windows --debug
+ env:
+ CMAKE_TOOLCHAIN_FILE: ${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+ - name: Install OpenCppCoverage
+ run: choco install opencppcoverage
+ shell: powershell
+
+ - name: Build Windows unit tests
+ working-directory: auth0_flutter/windows
+ run: |
+ cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DAUTH0_FLUTTER_ENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
+ cmake --build build --config Debug
+ shell: cmd
+
+ - name: Run Windows unit tests with coverage
+ working-directory: auth0_flutter/windows
+ run: |
+ & "C:\Program Files\OpenCppCoverage\OpenCppCoverage.exe" `
+ --sources ${{ github.workspace }}\auth0_flutter\windows `
+ --excluded_sources ${{ github.workspace }}\auth0_flutter\windows\test `
+ --export_type cobertura:coverage.xml `
+ --export_type html:coverage_html `
+ -- .\build\Debug\auth0_flutter_tests.exe
+ shell: powershell
+
+ - name: Upload coverage report
+ uses: actions/upload-artifact@v6
+ with:
+ name: Windows coverage
+ path: auth0_flutter/windows/coverage.xml
+
# test-android-smoke:
# name: Run native Android smoke tests using API-level ${{ matrix.android-api }}
# runs-on: macos-latest-xl
@@ -419,7 +485,8 @@ jobs:
test-auth0_flutter,
test-auth0_flutter_platform_interface,
test-ios-unit,
- test-android-unit
+ test-android-unit,
+ test-windows-unit
]
steps:
@@ -450,6 +517,12 @@ jobs:
name: Android coverage
path: coverage/android
+ - name: Download coverage report for Windows
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
+ with:
+ name: Windows coverage
+ path: coverage/windows
+
- name: Upload coverage report for auth0_flutter
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
with:
@@ -477,3 +550,10 @@ jobs:
name: Auth0 Flutter
flags: auth0_flutter_android
directory: coverage/android
+
+ - name: Upload coverage report for Windows
+ uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
+ with:
+ name: Auth0 Flutter
+ flags: auth0_flutter_windows
+ directory: coverage/windows
diff --git a/.gitignore b/.gitignore
index 93c50f1c7..2478e6f9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
# Global coverage
coverage/
+**/.vs/
appium-test/node_modules/*
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d33521a..000000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/auth0-flutter.iml b/.idea/auth0-flutter.iml
deleted file mode 100644
index 72b8b8ab1..000000000
--- a/.idea/auth0-flutter.iml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 52637d39f..000000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddfb..000000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/auth0_flutter/.metadata b/auth0_flutter/.metadata
index fe4a72344..7f5726441 100644
--- a/auth0_flutter/.metadata
+++ b/auth0_flutter/.metadata
@@ -8,3 +8,4 @@ version:
channel: stable
project_type: plugin
+
diff --git a/auth0_flutter/README.md b/auth0_flutter/README.md
index d2f8c45e1..fd188b4db 100644
--- a/auth0_flutter/README.md
+++ b/auth0_flutter/README.md
@@ -28,11 +28,11 @@ We're excited to announce the release of auth0_flutter v2.0.0!
### Requirements
-| Flutter | Android | iOS | macOS |
-| :---------- | :-------------- | :---------------- | :---------------- |
-| SDK 3.24.0+ | Android API 21+ | iOS 14+ | macOS 11+ |
-| Dart 3.5.0+ | Java 8+ | Swift 5.9+ | Swift 5.9+ |
-| | | Xcode 15.x / 16.x | Xcode 15.x / 16.x |
+| 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 |
+| | | Xcode 15.x / 16.x | Xcode 15.x / 16.x | vcpkg (for dependencies) |
### Installation
@@ -77,6 +77,7 @@ Under the **Application URIs** section of the **Settings** page, configure the f
- Android: `SCHEME://YOUR_DOMAIN/android/YOUR_PACKAGE_NAME/callback`
- iOS: `https://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback,YOUR_BUNDLE_ID://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback`
- macOS: `https://YOUR_DOMAIN/macos/YOUR_BUNDLE_ID/callback,YOUR_BUNDLE_ID://YOUR_DOMAIN/macos/YOUR_BUNDLE_ID/callback`
+- Windows: `https://YOUR_HOSTED_DOMAIN/callback` (or your custom callback URL on your intermediary server)
Example
@@ -86,11 +87,37 @@ If your Auth0 domain was `company.us.auth0.com` and your package name (Android)
- Android: `https://company.us.auth0.com/android/com.company.myapp/callback`
- iOS: `https://company.us.auth0.com/ios/com.company.myapp/callback,com.company.myapp://company.us.auth0.com/ios/com.company.myapp/callback`
- macOS: `https://company.us.auth0.com/macos/com.company.myapp/callback,com.company.myapp://company.us.auth0.com/macos/com.company.myapp/callback`
+- Windows: `https://your-app.example.com/callback` (your intermediary server endpoint)
+> π‘ **Windows**: The Windows implementation uses a custom scheme callback architecture (`auth0flutter://callback`). This requires an intermediary server to receive the Auth0 callback and forward it to your Windows app via the custom protocol. The intermediary server URL (e.g., `https://your-app.example.com/callback`) should be configured as the callback URL in your Auth0 dashboard. The server should handle the Auth0 redirect and trigger the `auth0flutter://` protocol to activate your app with the authorization code and state parameters.
+
Take note of the **client ID** and **domain** values under the **Basic Information** section. You'll need these values in the next step.
+##### Security Considerations for Custom URL Schemes
+
+> β οΈ **Important Security Information**
+>
+> Custom URL schemes (nonverifiable callback URIs) can be vulnerable to **app impersonation attacks**, where malicious apps could potentially intercept OAuth authorization codes by registering the same custom scheme on a device.
+>
+> **Recommended Best Practices:**
+>
+> - **Use HTTPS-based schemes whenever possible:**
+> - iOS 17.4+ / macOS 14.4+: Use Universal Links
+> - Android: Use Android App Links with HTTPS schemes
+> - These verifiable schemes cryptographically bind your app to your domain, preventing impersonation
+>
+> - **If you must use custom URL schemes:**
+> - Implement additional security measures such as PKCE (Proof Key for Code Exchange), which is automatically enabled in this SDK
+> - Consider using short-lived authorization codes
+> - Implement additional client-side validation
+> - Be aware that custom schemes offer no protection against malicious apps on the same device
+>
+> - **For Windows applications:** Custom schemes are currently required due to platform limitations. Ensure your intermediary server validates requests and uses secure communication
+>
+> π For more details about app impersonation risks and mitigation strategies, see [Auth0's Security Guidance: Measures Against App Impersonation](https://auth0.com/docs/secure/security-guidance/measures-against-app-impersonation)
+
#### π Web
Head to the [Auth0 Dashboard](https://manage.auth0.com/#/applications/) and create a new **Single Page** application.
@@ -127,7 +154,7 @@ Take note of the **client ID** and **domain** values under the **Basic Informati
### Configure the SDK
-#### π± Mobile/macOS
+#### π± Mobile/macOS/Windows
Start by importing `auth0_flutter/auth0_flutter.dart`.
@@ -247,6 +274,46 @@ If you have aΒ [custom domain](https://auth0.com/docs/customize/custom-domains),
> β οΈ For the associated domain to work, your app must be signed with your team certificate **even when building for the iOS simulator**. Make sure you are using the Apple Team whose Team ID is configured in the **Settings** page of your application.
+##### Windows: Configure protocol handler and intermediary server
+
+Windows authentication requires two components:
+
+1. **Custom Protocol Handler**: Your Windows app needs to register the `auth0flutter://` protocol handler
+2. **Intermediary Server**: A hosted server that receives the Auth0 callback and forwards it to your app
+
+**Step 1: Register the custom protocol handler**
+
+The `auth0flutter://` protocol should be automatically registered when your app is installed. The Flutter Windows plugin handles protocol activation through the `PLUGIN_STARTUP_URL` environment variable.
+
+**Step 2: Set up your intermediary server**
+
+Create a web endpoint (e.g., `https://your-app.example.com/callback`) that:
+1. Receives the Auth0 callback with `code` and `state` parameters
+2. Redirects to `auth0flutter://callback?code=...&state=...`
+
+Example server implementation:
+
+```javascript
+// Node.js/Express example
+app.get('/callback', (req, res) => {
+ const { code, state, error, error_description } = req.query;
+
+ if (error) {
+ res.redirect(`auth0flutter://callback?error=${error}&error_description=${error_description}`);
+ } else {
+ res.redirect(`auth0flutter://callback?code=${code}&state=${state}`);
+ }
+});
+```
+
+**Step 3: Configure Auth0 callback URLs**
+
+In your Auth0 application settings, add your intermediary server URL:
+- **Allowed Callback URLs**: `https://your-app.example.com/callback`
+- **Allowed Logout URLs**: `https://your-app.example.com/logout`
+
+The SDK will automatically use the `auth0flutter://callback` custom scheme internally to receive the forwarded callback from your server.
+
#### π Web
Start by importing `auth0_flutter/auth0_flutter_web.dart`.
@@ -271,24 +338,26 @@ Finally, in your `index.html` add the following `