Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
types: [opened, reopened, synchronize]

env:
FLUTTER_VERSION: '3.47.0'
FLUTTER_VERSION: '3.47.3'

jobs:

Expand Down
2 changes: 2 additions & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
10 changes: 7 additions & 3 deletions lib/src/auth/solid_auth_session_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
///
Expand Down
7 changes: 4 additions & 3 deletions lib/src/auth/solid_oidc_manager_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand Down Expand Up @@ -187,7 +188,7 @@ abstract class SolidOidcManagerFactory {
? OidcUserManager(
discoveryDocument: metadata.oidcMetadata,
clientCredentials: clientAuth,
store: OidcDefaultStore(),
store: createSolidTokenStore(),
settings: settings,
httpClient: config.httpClient,
keyStore: null,
Expand All @@ -198,7 +199,7 @@ abstract class SolidOidcManagerFactory {
Uri.parse(issuerUri),
),
clientCredentials: clientAuth,
store: OidcDefaultStore(),
store: createSolidTokenStore(),
settings: settings,
httpClient: config.httpClient,
keyStore: null,
Expand Down
60 changes: 60 additions & 0 deletions lib/src/auth/solid_token_store.dart
Original file line number Diff line number Diff line change
@@ -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,
),
);
5 changes: 4 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions support/flutter.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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"; \
Expand Down
16 changes: 16 additions & 0 deletions support/update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading