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 `