Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4c9a702
format the dart sources with the current formatter
hpoul Aug 25, 2026
39fe609
win32: support package:win32 6.x
hpoul Aug 25, 2026
b773e7a
ios/macos: add swift package manager support
hpoul Aug 25, 2026
18e4902
android: upgrade the toolchain and androidx dependencies
hpoul Aug 25, 2026
1733da3
document the android requirements as the conditions they actually are
hpoul Aug 25, 2026
773cb67
ci: run the suite on windows, and check formatting and analysis
hpoul Aug 25, 2026
6dc638f
example: move to the current sdk constraint and lints
hpoul Aug 25, 2026
f38d046
use a switch expression for the per-platform prompt info
hpoul Aug 25, 2026
e4b752d
add CLAUDE.md
hpoul Aug 25, 2026
89d5d39
correct the appcompat theme threshold to below api 28
hpoul Aug 25, 2026
8f89ddc
CLAUDE.md: spell out why the win32 coupling has no structural escape
hpoul Aug 25, 2026
d5b41a5
darwin: name the real author and licence in the podspec
hpoul Aug 25, 2026
5f4f6c9
ignore the swift package manager's per-user state
hpoul Aug 25, 2026
4115e59
darwin: carry the prompt reason on LAContext instead of kSecUseOperat…
hpoul Aug 25, 2026
1c06209
anchor the Package.resolved ignore to the plugin's own package
hpoul Aug 25, 2026
6a236e2
document that the darwin prompt strings are invisible under Face ID
hpoul Aug 25, 2026
19e0517
example: upgrade logging_appenders, and fix its widget test
hpoul Aug 25, 2026
466aa8a
android: upgrade the example to AGP 9.3.2, and hold biometric at alpha05
hpoul Aug 25, 2026
e9dd92b
darwin: always assign localizedReason, and cover win32 at runtime
hpoul Aug 25, 2026
7cf3f05
example: use jvmTarget rather than jvmToolchain(17)
hpoul Aug 25, 2026
e90c629
pin the windows credential prefix, and normalise line endings
hpoul Aug 25, 2026
9ee6916
exclude CLAUDE.md from the published package
hpoul Aug 25, 2026
a0489f1
make the package WebAssembly-ready
hpoul Aug 25, 2026
a6bcb29
stop the generated Podfiles reaching the published archive
hpoul Aug 25, 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
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# CI checks `dart format --set-exit-if-changed` on a Windows runner as well as
# on Linux. The formatter emits LF, so a checkout that converted line endings
# would fail that step — and would give contributors on Windows with
# core.autocrlf=true a working tree that never looks formatted.
*.dart text eol=lf

# Same reasoning for the files the build reads on more than one platform.
*.gradle text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.swift text eol=lf
*.podspec text eol=lf
83 changes: 43 additions & 40 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
@@ -1,51 +1,53 @@
name: Dart CI

on: [push]
on:
push:
pull_request:

jobs:
test:

runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v1
- uses: actions/setup-java@v1
with:
java-version: '12.x'
- uses: subosito/flutter-action@v1
with:
channel: 'stable' # or: 'dev' or 'beta'
- name: Install dependencies
run: flutter pub get
- name: Run tests
run: flutter test

web:
runs-on: windows-latest
# Windows is not incidental: the win32 bindings are compiled on every
# `dart.library.io` platform, so a `package:win32` break shows up as a
# broken iOS build. Running the suite on both catches it either way.
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable' # or: 'dev' or 'beta'
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed .
- name: Analyze
run: flutter analyze --fatal-infos
- name: Run tests
run: flutter test
# The example is a separate package: neither analyze nor test reaches it
# from the repository root, which is how its widget test sat broken
# against a UI it had never matched.
- name: Analyze the example
run: cd example && flutter analyze --fatal-infos
- name: Test the example
run: cd example && flutter test

build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
buildcommand: [linux]
os: [ubuntu-22.04]
exclude:
- os: ubuntu-22.04
buildcommand: linux
include:
# No macos build for now, have to configure code signing..
# - buildcommand: macos
# os: macos-latest
- buildcommand: ios
buildargs: --no-codesign
os: macos-latest
# No macos build: the example's keychain-access-groups entitlement
# resolves $(AppIdentifierPrefix) from the signing team, so the Runner
# cannot be built without a development certificate.
- buildcommand: windows
os: windows-latest
- buildcommand: linux
Expand All @@ -55,26 +57,27 @@ jobs:
- buildcommand: web
os: ubuntu-latest


steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable' # or: 'dev' or 'beta'
- uses: actions/setup-java@v3
channel: 'stable'
- uses: actions/setup-java@v4
if: matrix.buildcommand == 'appbundle'
with:
java-version: '17'
distribution: 'oracle'
- name: Patch for linux build
distribution: 'temurin'
- name: Install linux build dependencies
if: matrix.buildcommand == 'linux'
run: |
flutter doctor
sudo apt-get update -y
sudo apt-get install -y ninja-build libgtk-3-dev libsecret-1-dev
flutter doctor
- name: Enable build
if: matrix.buildcommand == 'macos' || matrix.buildcommand == 'linux'
run: flutter config --enable-${{ matrix.buildcommand }}-desktop
- name: Build for ${{ matrix.buildcommand }}
run: cd example && flutter build ${{ matrix.buildcommand }} ${{ matrix.buildargs }}
- name: Fail if CocoaPods was pulled back in
if: matrix.buildcommand == 'ios'
run: |
if [ -e "example/ios/Podfile" ]; then
echo "A Podfile was generated: the Swift Package Manager support regressed." >&2
exit 1
fi
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,11 @@ example/android/app/_debug-full-r8-config.txt

.gradletasknamecache

# Swift Package Manager leaves per-user Xcode state beside Package.swift.
.swiftpm/

# A dependency's Package.resolved is never read — only the root package's is,
# and here that is the app's FlutterGeneratedPluginSwiftPackage. Anchored, so
# that an app-level one under example/*/Runner.xcodeproj stays committable.
/darwin/biometric_storage/Package.resolved

40 changes: 40 additions & 0 deletions .pubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Consulted by `pub publish` INSTEAD OF the .gitignore in THIS directory. Nested
# .gitignore files are still honoured — example/ios/.gitignore and friends keep
# working — so only the root .gitignore's rules have to be restated here, and
# they are, below. Getting that wrong is not theoretical: a one-line version of
# this file dropped the root rules and shipped .dart_tool artifacts.
#
# The check that matters, and it has to be run against a DIRTY tree — after a
# build, a pod install and a dartdoc run, not a clean checkout:
# flutter pub publish --dry-run
# The archive must differ from the .gitignore-only one by CLAUDE.md alone.

# The reason this file exists: maintainer-facing instructions, of no use to
# anyone consuming the package.
CLAUDE.md

# Build output. Unanchored so it also catches example/build.
build/
out/
.dart_tool/
.gradle/
.gradletasknamecache
.flutter-plugins-dependencies
.packages
.pub/
node_modules/
example/android/app/_debug-full-r8-config.txt

# dartdoc output. Not in .gitignore either, and it lands next to the committed
# doc/screenshot_ios.png as thousands of files.
doc/api/

# Editor and OS noise.
.idea/
.vscode/
*.iml
.DS_Store

# Swift Package Manager per-user state.
.swiftpm/
/darwin/biometric_storage/Package.resolved
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,54 @@
## 6.0.0-dev.1

**Breaking**: requires Dart 3.10 / Flutter 3.44 or newer.

* Support `package:win32` 6.x, and drop 5.x and older. Every 5.x release of this
plugin pinned `win32 >=2.0.0 <6.0.0`, which made it unresolvable alongside
`package_info_plus >=10.1.0` and anything else on win32 6. The Windows
implementation is compiled on every `dart.library.io` platform, so win32 6
removing `TEXT()` also broke iOS and macOS builds — the test suite now imports
the public barrel so `flutter test` compiles the win32 bindings on any host.
* windows: writing an empty value no longer throws.
* windows: fix a use-after-free in `read()`. `CredentialBlob.asTypedList()` is a
view onto memory owned by the credential, and it was decoded *after* `CredFree`
had released it. The bytes are now copied out first. Present since 1.1.0,
where a9e3944 moved the `CredFree` call in between the two.
* windows: the bindings now have runtime coverage, not just compile coverage —
`test/biometric_storage_win32_test.dart` exercises write/read/delete against
the real credential store, and runs on the Windows CI job.
* iOS/macOS: Swift Package Manager support. Adding this plugin to an app that has
migrated to SwiftPM no longer regenerates a `Podfile`. CocoaPods keeps working;
both a `Package.swift` and a podspec are shipped.
* iOS/macOS: the Swift sources moved to `darwin/` and are shared through
`sharedDarwinSource`, replacing the symlink from `ios/Classes`. The macOS
plugin class is now `BiometricStoragePlugin` (was
`BiometricStorageMacOSPlugin`) and the Objective-C shim on iOS is gone. Neither
is referenced from Dart, so this is only visible in a hand-written registrant.
* android: AGP 8.13, Kotlin 2.2, compileSdk 36, `androidx.biometric`
1.4.0-alpha05, `core-ktx` 1.18.0, `fragment-ktx` 1.9.0, slf4j 2.0.18 and
kotlin-logging 8. The plugin no longer applies the Kotlin Gradle Plugin itself
— AGP 9 warns about that and future Flutter releases reject it.
* android: `canAuthenticate()` no longer throws on a status code the plugin does
not know about — Android 16 added `BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS` (21)
and every call blew up. Unmapped codes are reported as
`CanAuthenticateResponse.statusUnknown` and logged.
https://github.com/authpass/biometric_storage/issues/148
* android: the plugin no longer calls `jvmToolchain`, which failed to resolve in
some consumer builds. https://github.com/authpass/biometric_storage/issues/107
* Document the Android `FlutterFragmentActivity` and `Theme.AppCompat`
requirements as what they actually are: both only apply to storage that shows
an authentication prompt, and the theme only where `androidx.biometric` falls
back to its own dialog, which is below API 28 rather than below API 29.
* web: the package is now WebAssembly-ready. `lib/src/biometric_storage.dart`
imported `dart:io` unconditionally for `Platform`, which marked the whole
package wasm-incompatible; the host OS now comes through a conditional import,
with `dart:io` still doing the work everywhere it exists. No behaviour change
on any platform. https://github.com/authpass/biometric_storage/issues/145
* iOS/macOS: the prompt strings (`IosPromptInfo.saveTitle` / `accessTitle`) now
travel on the `LAContext` as `localizedReason` instead of through
`kSecUseOperationPrompt`, deprecated since iOS 14 / macOS 11. Same prompts, no
deprecation warnings on build.

## 5.2.0-dev.1

* iOS/macOS: `StorageFileInitOptions.darwinKeychainAccessGroup` to store items
Expand Down
Loading
Loading