diff --git a/.lycheeignore b/.lycheeignore index b7cb4f76..49be24e1 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -84,6 +84,7 @@ https://github.com/anusii/solidpod/blob/main/solidpodeg/README.md https://server/POD_NAME/APP_NAME/data/FILE_PATH https://server/alice/ https://server/alice/myapp/ +https://anusii.github.io/solidpodeg/client-profile.jsonld # 20260605 gjw Failing solid servers diff --git a/example/client-profile.jsonld b/example/client-profile.jsonld new file mode 100644 index 00000000..e56cc404 --- /dev/null +++ b/example/client-profile.jsonld @@ -0,0 +1,25 @@ +{ + "@context": "https://www.w3.org/ns/solid/oidc-context.jsonld", + "client_id": "https://anusii.github.io/solidpodeg/client-profile.jsonld", + "client_name": "Solid Pod Demonstrator", + "application_type": "native", + "redirect_uris": [ + "https://anusii.github.io/solidpodeg/redirect.html", + "com.example.solidpodeg://redirect", + "http://localhost:4400/redirect.html" + ], + "post_logout_redirect_uris": [ + "https://anusii.github.io/solidpodeg/redirect.html", + "com.example.solidpodeg://redirect", + "http://localhost:4400/redirect.html" + ], + "scope": "openid profile offline_access webid", + "grant_types": [ + "authorization_code", + "refresh_token" + ], + "response_types": [ + "code" + ], + "token_endpoint_auth_method": "none" +} diff --git a/example/lib/constants/app.dart b/example/lib/constants/app.dart index 1827ea31..aaf133a0 100644 --- a/example/lib/constants/app.dart +++ b/example/lib/constants/app.dart @@ -25,6 +25,7 @@ library; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; const titleBackgroundColor = Color(0xFFF0E4D7); @@ -57,17 +58,30 @@ demo:exampleData$fileName '''; } -const clientIdVal = - 'https://anushkavidanage.github.io/solidpod/example/client-profile.jsonld'; +/// URL of the Solid-OIDC client identifier document. Must be publicly readable +/// and its own `client_id` field must equal this URL. The source document lives +/// in the project root as `client-profile.jsonld`. +const clientIdVal = 'https://anusii.github.io/solidpodeg/client-profile.jsonld'; -const redirectUrisList = [ - 'https://anushkavidanage.github.io/solidpod/example/redirect.html', - 'http://localhost:4400/redirect', - 'com.example.demopod://redirect', // Was com.example.solidpodeg -]; +/// Redirect URIs offered to the Solid-OIDC flow. +List get redirectUrisList { + if (kIsWeb) { + return ['${Uri.base.origin}/redirect.html']; + } + return const [ + 'com.example.solidpodeg://redirect', + 'http://localhost:4400/redirect.html', + ]; +} -const postLogoutRedirectUrisList = [ - 'https://anushkavidanage.github.io/solidpod/example/redirect.html', - 'http://localhost:4400/redirect', - 'com.example.demopod://redirect', // Was com.example.solidpodeg -]; +/// Post-logout redirect URIs, derived the same origin-aware way as +/// [redirectUrisList]. +List get postLogoutRedirectUrisList { + if (kIsWeb) { + return ['${Uri.base.origin}/redirect.html']; + } + return const [ + 'com.example.solidpodeg://redirect', + 'http://localhost:4400/redirect.html', + ]; +} diff --git a/example/support/flutter.mk b/example/support/flutter.mk index 7e7cf942..bb718793 100644 --- a/example/support/flutter.mk +++ b/example/support/flutter.mk @@ -73,7 +73,7 @@ help:: .PHONY: chrome chrome: - flutter run -d chrome + flutter run -d chrome --release --web-port=4400 # 20220503 gjw The following fails if the target files already exist - # just needs to be run once. diff --git a/lib/src/solid/authenticate.dart b/lib/src/solid/authenticate.dart index 8915c4e3..b3dd4961 100644 --- a/lib/src/solid/authenticate.dart +++ b/lib/src/solid/authenticate.dart @@ -47,11 +47,12 @@ import 'package:solidpod/src/solid/utils/exceptions.dart' import 'package:solidpod/src/solid/utils/misc.dart' show isUserLoggedIn; /// Selects the appropriate redirect URI from [uris] based on the runtime -/// platform, using the URI format as the discriminator: +/// platform, using the URI format (and, on web, the origin) as the +/// discriminator: /// /// | Platform | Matched format | /// |---|---| -/// | Web | `https://` URI (same-origin BroadcastChannel requirement) | +/// | Web | The entry whose origin equals the app's current origin ([Uri.base]) | /// | Android / iOS / macOS | Custom scheme URI (not `http://` or `https://`) | /// | Desktop (Windows / Linux) | `http://localhost` loopback URI | /// @@ -64,9 +65,20 @@ String pickRedirectUri(List uris) { if (uris.length == 1) return uris.first; if (kIsWeb) { + final currentOrigin = Uri.base.origin; return uris.firstWhere( - (u) => u.startsWith('https://'), - orElse: () => uris.first, + (u) { + final parsed = Uri.tryParse(u); + if (parsed == null) return false; + if (!parsed.isScheme('http') && !parsed.isScheme('https')) { + return false; + } + return parsed.origin == currentOrigin; + }, + orElse: () => uris.firstWhere( + (u) => u.startsWith('https://'), + orElse: () => uris.first, + ), ); } if (defaultTargetPlatform == TargetPlatform.android ||