From 57d2865e5a7793b61d890a2ec67b7a9ce03b024c Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Mon, 22 Jun 2026 20:34:03 -0400 Subject: [PATCH] refactor: adopt granular scopes! --- .../repositories/auth_repository_impl.dart | 23 ++++- scripts/codex-cloud-maintenance.sh | 57 +++++++++++++ scripts/codex-cloud-setup.sh | 83 +++++++++++++++++++ .../auth_repository_impl_test.dart | 16 ++-- 4 files changed, 173 insertions(+), 6 deletions(-) create mode 100755 scripts/codex-cloud-maintenance.sh create mode 100755 scripts/codex-cloud-setup.sh diff --git a/lib/src/core/auth/data/repositories/auth_repository_impl.dart b/lib/src/core/auth/data/repositories/auth_repository_impl.dart index e747e504..d8a57edd 100644 --- a/lib/src/core/auth/data/repositories/auth_repository_impl.dart +++ b/lib/src/core/auth/data/repositories/auth_repository_impl.dart @@ -29,8 +29,29 @@ const Duration _refreshLeeway = Duration(minutes: 5); const String _randomCharset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; +String _buildServiceDid(String serviceUrl, String serviceId) { + final uri = Uri.parse(serviceUrl); + return 'did:web:${uri.host}#$serviceId'; +} + List _buildAipScopes() { - return const ['atproto', 'transition:generic']; + final sprkAppViewDid = _buildServiceDid(AppConfig.appViewUrl, 'sprk_appview'); + final bskyAppViewDid = _buildServiceDid( + AppConfig.bskyAppViewUrl, + 'bsky_appview', + ); + return [ + 'atproto', + 'include:so.sprk.authFullApp?aud=$sprkAppViewDid', + 'include:app.bsky.authViewAll?aud=$bskyAppViewDid', + 'include:app.bsky.authCreatePosts?aud=$bskyAppViewDid', + 'include:app.bsky.authDeleteContent?aud=$bskyAppViewDid', + 'blob:*/*', + 'repo:app.bsky.feed.like', + 'repo:app.bsky.feed.repost', + 'repo:app.bsky.graph.follow', + 'rpc:com.atproto.moderation.createReport?aud=*', + ]; } String _buildAipScope() => _buildAipScopes().join(' '); diff --git a/scripts/codex-cloud-maintenance.sh b/scripts/codex-cloud-maintenance.sh new file mode 100755 index 00000000..69f0843c --- /dev/null +++ b/scripts/codex-cloud-maintenance.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$repo_root" + +flutter_version="${FLUTTER_VERSION:-3.44.0}" +flutter_sdk_dir="${FLUTTER_SDK_DIR:-$HOME/.local/flutter-$flutter_version}" + +run() { + printf '\n==> %s\n' "$*" + "$@" +} + +flutter_matches_expected_version() { + command -v flutter >/dev/null 2>&1 || return 1 + flutter --version 2>/dev/null | head -n 1 | grep -Fq "Flutter $flutter_version" +} + +ensure_flutter_from_cache() { + if [[ -x "$flutter_sdk_dir/bin/flutter" ]]; then + export PATH="$flutter_sdk_dir/bin:$PATH" + fi + + if flutter_matches_expected_version; then + return + fi + + printf 'Flutter %s is not available in this cached container.\n' "$flutter_version" >&2 + printf 'Reset the Codex environment cache so the setup script can reinstall it.\n' >&2 + exit 1 +} + +ensure_env_file() { + if [[ -f .env ]]; then + return + fi + + if [[ -f .env.example ]]; then + cp .env.example .env + else + touch .env + fi +} + +ensure_flutter_from_cache +ensure_env_file + +run flutter --version +run flutter pub get --enforce-lockfile +run flutter gen-l10n +run dart run build_runner build --delete-conflicting-outputs + +( + cd widgetbook + run dart run build_runner build --delete-conflicting-outputs +) diff --git a/scripts/codex-cloud-setup.sh b/scripts/codex-cloud-setup.sh new file mode 100755 index 00000000..8e852054 --- /dev/null +++ b/scripts/codex-cloud-setup.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$repo_root" + +flutter_version="${FLUTTER_VERSION:-3.44.0}" +flutter_sdk_dir="${FLUTTER_SDK_DIR:-$HOME/.local/flutter-$flutter_version}" + +run() { + printf '\n==> %s\n' "$*" + "$@" +} + +persist_flutter_path() { + local export_line="export PATH=\"$flutter_sdk_dir/bin:\$PATH\"" + + mkdir -p "$HOME/.local/bin" + for profile in "$HOME/.bashrc" "$HOME/.profile"; do + touch "$profile" + if ! grep -Fqx "$export_line" "$profile"; then + printf '\n%s\n' "$export_line" >> "$profile" + fi + done +} + +flutter_matches_expected_version() { + command -v flutter >/dev/null 2>&1 || return 1 + flutter --version 2>/dev/null | head -n 1 | grep -Fq "Flutter $flutter_version" +} + +ensure_flutter() { + if flutter_matches_expected_version; then + return + fi + + if [[ -x "$flutter_sdk_dir/bin/flutter" ]]; then + export PATH="$flutter_sdk_dir/bin:$PATH" + if flutter_matches_expected_version; then + persist_flutter_path + return + fi + fi + + if [[ -e "$flutter_sdk_dir" ]]; then + printf 'Expected Flutter %s, but %s already exists and does not match.\n' "$flutter_version" "$flutter_sdk_dir" >&2 + printf 'Reset the Codex environment cache or set FLUTTER_SDK_DIR to a clean path.\n' >&2 + exit 1 + fi + + run git clone --depth 1 --branch "$flutter_version" https://github.com/flutter/flutter.git "$flutter_sdk_dir" + export PATH="$flutter_sdk_dir/bin:$PATH" + persist_flutter_path +} + +ensure_env_file() { + if [[ -f .env ]]; then + return + fi + + if [[ -f .env.example ]]; then + cp .env.example .env + else + touch .env + fi +} + +ensure_flutter +ensure_env_file + +run flutter --version +run dart --version +run flutter config --no-analytics +run dart --disable-analytics + +run flutter pub get --enforce-lockfile +run flutter gen-l10n +run dart run build_runner build --delete-conflicting-outputs + +( + cd widgetbook + run dart run build_runner build --delete-conflicting-outputs +) diff --git a/test/src/core/auth/data/repositories/auth_repository_impl_test.dart b/test/src/core/auth/data/repositories/auth_repository_impl_test.dart index 1abb49ca..9cf15668 100644 --- a/test/src/core/auth/data/repositories/auth_repository_impl_test.dart +++ b/test/src/core/auth/data/repositories/auth_repository_impl_test.dart @@ -663,7 +663,7 @@ void main() { ); expect( registrationBody['scope'] as String, - 'atproto transition:generic', + 'atproto include:so.sprk.authFullApp?aud=did:web:api.sprk.so#sprk_appview include:app.bsky.authViewAll?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authCreatePosts?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authDeleteContent?aud=did:web:api.bsky.app#bsky_appview blob:*/* repo:app.bsky.feed.like repo:app.bsky.feed.repost repo:app.bsky.graph.follow rpc:com.atproto.moderation.createReport?aud=*', ); return http.Response( json.encode({ @@ -709,7 +709,10 @@ void main() { expect(authUri.queryParameters['login_hint'], 'alice.sprk.so'); expect(authUri.queryParameters['code_challenge'], isNotEmpty); expect(authUri.queryParameters['state'], isNotEmpty); - expect(authUri.queryParameters['scope'], 'atproto transition:generic'); + expect( + authUri.queryParameters['scope'], + 'atproto include:so.sprk.authFullApp?aud=did:web:api.sprk.so#sprk_appview include:app.bsky.authViewAll?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authCreatePosts?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authDeleteContent?aud=did:web:api.bsky.app#bsky_appview blob:*/* repo:app.bsky.feed.like repo:app.bsky.feed.repost repo:app.bsky.graph.follow rpc:com.atproto.moderation.createReport?aud=*', + ); final callbackUrl = Uri.parse(_redirectUri) .replace( @@ -839,7 +842,7 @@ void main() { json.decode(request.body) as Map; expect( registrationBody['scope'] as String, - 'atproto transition:generic', + 'atproto include:so.sprk.authFullApp?aud=did:web:api.sprk.so#sprk_appview include:app.bsky.authViewAll?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authCreatePosts?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authDeleteContent?aud=did:web:api.bsky.app#bsky_appview blob:*/* repo:app.bsky.feed.like repo:app.bsky.feed.repost repo:app.bsky.graph.follow rpc:com.atproto.moderation.createReport?aud=*', ); return http.Response( json.encode({ @@ -864,7 +867,10 @@ void main() { final authUri = Uri.parse(authUrl); expect(registrationCalls, 1); - expect(authUri.queryParameters['scope'], 'atproto transition:generic'); + expect( + authUri.queryParameters['scope'], + 'atproto include:so.sprk.authFullApp?aud=did:web:api.sprk.so#sprk_appview include:app.bsky.authViewAll?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authCreatePosts?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authDeleteContent?aud=did:web:api.bsky.app#bsky_appview blob:*/* repo:app.bsky.feed.like repo:app.bsky.feed.repost repo:app.bsky.graph.follow rpc:com.atproto.moderation.createReport?aud=*', + ); final savedSnapshot = AuthSnapshot.fromJsonString( (await storage.getString(StorageKeys.account))!, @@ -872,7 +878,7 @@ void main() { expect(savedSnapshot.aipClientRegistration?.clientId, 'client-2'); expect( savedSnapshot.aipClientRegistration?.scope, - 'atproto transition:generic', + 'atproto include:so.sprk.authFullApp?aud=did:web:api.sprk.so#sprk_appview include:app.bsky.authViewAll?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authCreatePosts?aud=did:web:api.bsky.app#bsky_appview include:app.bsky.authDeleteContent?aud=did:web:api.bsky.app#bsky_appview blob:*/* repo:app.bsky.feed.like repo:app.bsky.feed.repost repo:app.bsky.graph.follow rpc:com.atproto.moderation.createReport?aud=*', ); }, ); -- 2.51.2