diff --git a/lib/solidpod.dart b/lib/solidpod.dart index f9bfd4d5..f2b2d9ab 100644 --- a/lib/solidpod.dart +++ b/lib/solidpod.dart @@ -49,7 +49,8 @@ export 'src/solid/constants/predicates.dart'; /// Solid authentication function -export 'src/solid/authenticate.dart' show solidAuthenticate; +export 'src/solid/authenticate.dart' + show cancelSolidAuthenticate, isSolidAuthenticatePending, solidAuthenticate; /// Status class to represent different function outputs @@ -86,7 +87,8 @@ export 'src/solid/utils/exceptions.dart' NotLoggedInException, ResourceNotDecryptableException, ResourceNotExistException, - SecurityKeyNotAvailableException; + SecurityKeyNotAvailableException, + SolidAuthCancelledException; /// Includes common TTL conversion functions such as parseTTLMap. diff --git a/lib/src/solid/authenticate.dart b/lib/src/solid/authenticate.dart index ef23b4c5..be90e05a 100644 --- a/lib/src/solid/authenticate.dart +++ b/lib/src/solid/authenticate.dart @@ -32,6 +32,7 @@ library; +import 'dart:async' show unawaited; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -41,6 +42,8 @@ import 'package:solid_auth/solid_auth.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart' show AuthDataManager; +import 'package:solidpod/src/solid/utils/exceptions.dart' + show SolidAuthCancelledException; import 'package:solidpod/src/solid/utils/misc.dart' show isUserLoggedIn, logoutPod; @@ -53,6 +56,20 @@ final List _scopes = [ 'webid', // web ID is necessary to get refresh token ]; +/// Returns true while [solidAuthenticate] is awaiting the browser-based OAuth +/// flow. + +bool isSolidAuthenticatePending() => isAuthenticatePending(); + +/// Aborts any in-flight [solidAuthenticate] call. Delegates to +/// `solid_auth.cancelAuthenticate()` which closes the local OAuth callback +/// server and errors the pending awaiter, so this caller unwinds with a +/// [SolidAuthCancelledException]. + +void cancelSolidAuthenticate() { + unawaited(cancelAuthenticate()); +} + /// Asynchronously authenticate a user against a Solid server [serverId]. /// /// [serverId] is an issuer URI and is essential for the @@ -85,6 +102,7 @@ Future?> solidAuthenticate( // Authentication process for the POD issuer. final issuerUri = await getIssuer(serverId); + authData = await authenticate(Uri.parse(issuerUri), _scopes, context); // Validate authentication response before saving @@ -130,6 +148,8 @@ Future?> solidAuthenticate( final profData = utf8.decode(await getResource(profCardUrl)); return [authData, webId, profData]; + } on AuthCancelledException catch (e) { + throw SolidAuthCancelledException(e.message); } on Object catch (e) { debugPrint('Solid Authenticate Failed: $e'); return null; diff --git a/lib/src/solid/utils/exceptions.dart b/lib/src/solid/utils/exceptions.dart index 86d80d61..3726f66b 100644 --- a/lib/src/solid/utils/exceptions.dart +++ b/lib/src/solid/utils/exceptions.dart @@ -81,3 +81,17 @@ class SecurityKeyNotAvailableException implements Exception { @override String toString() => 'SecurityKeyNotAvailableException: $message'; } + +/// Thrown by [solidAuthenticate] when the in-flight authentication is aborted +/// by [cancelSolidAuthenticate]. Callers can catch this to distinguish a +/// deliberate cancellation from a genuine authentication failure such as a +/// network error. + +class SolidAuthCancelledException implements Exception { + final String message; + + SolidAuthCancelledException([this.message = 'Authentication was cancelled']); + + @override + String toString() => 'SolidAuthCancelledException: $message'; +} diff --git a/lib/src/solid/utils/init_helper.dart b/lib/src/solid/utils/init_helper.dart index 3f06ca32..7204b806 100644 --- a/lib/src/solid/utils/init_helper.dart +++ b/lib/src/solid/utils/init_helper.dart @@ -98,6 +98,10 @@ Future clearPodStructureInitialised() async { if (await secureStorage.containsKey(key: key)) { await secureStorage.delete(key: key); } + } on NotLoggedInException { + // Expected during account-switch flows where the caller logs out before + // clearing the flag. There is no user-specific key to delete, so this is + // a no-op rather than an error. } on Object catch (e) { debugPrint('clearPodStructureInitialised() failed: $e'); } diff --git a/pubspec.yaml b/pubspec.yaml index ec2a6372..7263b739 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,12 @@ dev_dependencies: flutter_lints: ^6.0.0 import_order_lint: ^0.2.2 +dependency_overrides: + solid_auth: + git: + url: https://github.com/anusii/solid_auth.git + ref: tony/630_try_another_webid + flutter: assets: - assets/images/default_image.jpg