From 08a91f65d4a38e0fde5d9036038435a0f81011d3 Mon Sep 17 00:00:00 2001 From: Graham Williams Date: Fri, 11 Sep 2026 15:20:57 +1000 Subject: [PATCH 1/3] Updated templates --- .github/workflows/ci.yaml | 2 +- .lycheeignore | 2 ++ support/flutter.mk | 2 ++ support/update.sh | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3f46e97..7f3c7ec 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,7 +13,7 @@ on: types: [opened, reopened, synchronize] env: - FLUTTER_VERSION: '3.47.0' + FLUTTER_VERSION: '3.47.3' jobs: diff --git a/.lycheeignore b/.lycheeignore index a68957c..5ef0661 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -101,6 +101,8 @@ https://solid.dev.empwr.au/alice/profile/card#me https://solid.dev.empwr.au/Analyser https://www.heart.org/ https://www.heart.org/en/health-topics/high-blood-pressure/understanding-blood-pressure-readings +https://solid.dev.empwr.au/bob/profile/card#me +https://snapcraft.io/healthpod # 20260605 gjw Failing solid servers diff --git a/support/flutter.mk b/support/flutter.mk index 8ea0ec5..27e7968 100644 --- a/support/flutter.mk +++ b/support/flutter.mk @@ -317,6 +317,8 @@ todo: license: @echo "Files without a LICENSE:\n" @-output=$$(find lib -type f -not -name '*~' -not -name 'README*' -not -name '*.g.dart' \ + -not -name '*.pb.dart' -not -name '*.pbenum.dart' \ + -not -name '*.pbjson.dart' -not -name '*.pbgrpc.dart' -not -name '*.proto' \ ! -exec grep -qE '^(///? Copyright|///? Licensed)' {} \; -print | xargs printf "\t%s\n"); \ if [ $$(echo "$$output" | wc -w) -ne 0 ]; then \ echo "$$output"; \ diff --git a/support/update.sh b/support/update.sh index d5fd6ad..e36b3f5 100644 --- a/support/update.sh +++ b/support/update.sh @@ -37,6 +37,22 @@ FILES=( ${SCRIPTS}Makefile Makefile ) +# 20260911 gjw The android build configuration is app independent: the +# app name only appears in android/app/build.gradle.kts (namespace, +# applicationId) which stays local. Sharing these keeps the gradle, +# AGP, and kotlin versions in step across all apps, which is what +# flutter's build dependency validation warns about. Only add them +# when the app builds for android. + +if [ -d android ]; then + FILES+=( + ${SCRIPTS}flutter/android/build.gradle.kts android/build.gradle.kts + ${SCRIPTS}flutter/android/settings.gradle.kts android/settings.gradle.kts + ${SCRIPTS}flutter/android/gradle.properties android/gradle.properties + ${SCRIPTS}flutter/android/gradle/wrapper/gradle-wrapper.properties android/gradle/wrapper/gradle-wrapper.properties + ) +fi + length=${#FILES[@]} for ((i=0; i < length; i+=2)); do From ce8206384cbbd360c55890d1a1c3747610b48d34 Mon Sep 17 00:00:00 2001 From: Graham Williams Date: Fri, 11 Sep 2026 15:36:31 +1000 Subject: [PATCH 2/3] Store the tokens and DPoP key in the platform keystore --- CHANGELOG.md | 1 + lib/src/auth/solid_auth_session_store.dart | 10 +++- lib/src/auth/solid_oidc_manager_factory.dart | 7 ++- lib/src/auth/solid_token_store.dart | 60 ++++++++++++++++++++ pubspec.yaml | 3 + 5 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 lib/src/auth/solid_token_store.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bd1bf5..a6bc915 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ utilised by the flutter version_widget package. ## 1.0 Migrate to using OIDC OpenID certified ++ Store the tokens and DPoP key in the platform keystore [1.0.8 20260911 gjw] + Review and cleanup solidautheg [1.0.7 20260904 gjw] + Migrate oidc from 0->4 [1.0.6 20260904 anuskavidanage] + Streamline login logout [1.0.5 20260828 anushkavidanage] diff --git a/lib/src/auth/solid_auth_session_store.dart b/lib/src/auth/solid_auth_session_store.dart index b4d375e..e5b5df1 100644 --- a/lib/src/auth/solid_auth_session_store.dart +++ b/lib/src/auth/solid_auth_session_store.dart @@ -33,6 +33,8 @@ import 'package:logging/logging.dart'; import 'package:oidc_core/oidc_core.dart'; import 'package:oidc_default_store/oidc_default_store.dart'; +import 'package:solid_auth/src/auth/solid_token_store.dart'; + final _log = Logger('solid_auth.SolidAuthSessionStore'); /// Holds the Solid-specific parameters needed to restore a previous @@ -77,15 +79,17 @@ class SolidAuthSessionData { /// | `solid_auth_rsa_public` | PEM-encoded RSA public key | /// /// The OIDC access/refresh tokens themselves are stored by `package:oidc` -/// automatically in the same underlying secure storage โ€” this class only -/// tracks the Solid-specific extras needed to reconstruct the manager. +/// in the same namespace of the same store โ€” this class only tracks the +/// Solid-specific extras needed to reconstruct the manager. Note that the +/// private key above is a credential: the store must be the keystore-backed +/// one from [createSolidTokenStore], never a bare [OidcDefaultStore]. class SolidAuthSessionStore { static const _issuerUriKey = 'solid_auth_issuer_uri'; static const _scopesKey = 'solid_auth_scopes'; static const _privateKeyKey = 'solid_auth_rsa_private'; static const _publicKeyKey = 'solid_auth_rsa_public'; - final _store = OidcDefaultStore(); + final _store = createSolidTokenStore(); /// Persists all parameters required to restore this session later. /// diff --git a/lib/src/auth/solid_oidc_manager_factory.dart b/lib/src/auth/solid_oidc_manager_factory.dart index 10292ad..f7ccd7f 100644 --- a/lib/src/auth/solid_oidc_manager_factory.dart +++ b/lib/src/auth/solid_oidc_manager_factory.dart @@ -32,6 +32,7 @@ import 'package:oidc/oidc.dart'; import 'package:oidc_default_store/oidc_default_store.dart'; import 'package:solid_auth/src/auth/solid_oidc_config.dart'; +import 'package:solid_auth/src/auth/solid_token_store.dart'; import 'package:solid_auth/src/dpop/dpop_key_manager.dart'; import 'package:solid_auth/src/dpop/dpop_token_generator.dart'; import 'package:solid_auth/src/models/solid_provider_metadata.dart'; @@ -47,7 +48,7 @@ final _log = Logger('solid_auth.SolidOidcManagerFactory'); /// - Solid-specific discovery document wrapping. /// - Correct scope defaults (`webid` always included). /// - DPoP-ready token hooks (wired in separately via [SolidDpopHook]). -/// - Platform-appropriate storage via [OidcDefaultStore]. +/// - Keystore-backed token storage via [OidcDefaultStore]. /// /// Example: /// ```dart @@ -187,7 +188,7 @@ abstract class SolidOidcManagerFactory { ? OidcUserManager( discoveryDocument: metadata.oidcMetadata, clientCredentials: clientAuth, - store: OidcDefaultStore(), + store: createSolidTokenStore(), settings: settings, httpClient: config.httpClient, keyStore: null, @@ -198,7 +199,7 @@ abstract class SolidOidcManagerFactory { Uri.parse(issuerUri), ), clientCredentials: clientAuth, - store: OidcDefaultStore(), + store: createSolidTokenStore(), settings: settings, httpClient: config.httpClient, keyStore: null, diff --git a/lib/src/auth/solid_token_store.dart b/lib/src/auth/solid_token_store.dart new file mode 100644 index 0000000..a2d1632 --- /dev/null +++ b/lib/src/auth/solid_token_store.dart @@ -0,0 +1,60 @@ +/// Support for flutter apps authenticating to a Solid server. +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the MIT License (the "License"). +/// +/// License: https://choosealicense.com/licenses/mit/. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/// +/// Authors: Graham Williams +library; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:oidc_default_store/oidc_default_store.dart'; + +/// Builds the store used for everything solid_auth persists between runs. +/// +/// Every [OidcDefaultStore] in solid_auth must come from here. The +/// [FlutterSecureStorage] instance is required, not optional: without it +/// [OidcDefaultStore] falls back to `package:shared_preferences` for its +/// `secureTokens` namespace, and that namespace holds the whole set of +/// credentials โ€” the access, refresh and ID tokens and the PKCE +/// `code_verifier` written by `package:oidc`, plus the DPoP RSA private key +/// written by `SolidAuthSessionStore`. On disk in the clear, the refresh token +/// lets anything able to read the app's preferences file mint access tokens +/// for the user's POD without their password, and the DPoP private key is what +/// binds those tokens to this client, so leaking the pair is full +/// impersonation. Ordinary home-directory backups collect the file +/// (RFC 9700 ยง4.14). +/// +/// The per-platform options are [OidcDefaultStore]'s own hardened +/// recommendations: an Android-Keystore-backed key on Android, and +/// first-unlock-this-device keychain items (never iCloud-synced) on iOS and +/// macOS. Note that macOS additionally requires the Keychain Sharing +/// entitlement for `flutter_secure_storage` to function at all. + +OidcDefaultStore createSolidTokenStore() => OidcDefaultStore( + secureStorageInstance: const FlutterSecureStorage( + aOptions: OidcDefaultStore.recommendedAndroidOptions, + iOptions: OidcDefaultStore.recommendedIOSOptions, + mOptions: OidcDefaultStore.recommendedMacOsOptions, + ), +); diff --git a/pubspec.yaml b/pubspec.yaml index d9acecb..0f9b862 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,9 @@ dependencies: # Crypto package crypto: ^3.0.7 + # Keystore-backed storage for the OIDC tokens, passed to OidcDefaultStore + flutter_secure_storage: ^10.3.1 + # Core OIDC package (OpenID certified) replacing the forked openid_client oidc: ^4.0.0 oidc_core: ^3.0.0 From 4487efe01607a629cea4c13793cbd5a3505a37a4 Mon Sep 17 00:00:00 2001 From: Graham Williams Date: Sat, 12 Sep 2026 05:33:52 +1000 Subject: [PATCH 3/3] Bump version 1.0.8 Store the tokens and DPoP key in the platform keystore --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 0f9b862..cbf93bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: solid_auth description: Authenticate to a Solid POD server using Solid-OIDC with certified oidc. -version: 1.0.7 +version: 1.0.8 homepage: https://github.com/anusii/solid_auth repository: https://github.com/anusii/solid_auth