feat: update OAuth config for private-use URI scheme

Switch from development callback URL to proper atproto-compliant
private-use URI scheme (RFC 8252):
- Scheme: social.coves:/callback (single slash per spec)
- Works on both Android and iOS without Universal Links complexity

Platform changes:
- Android: Update CallbackActivity intent filter scheme
- iOS: Update CFBundleURLSchemes in Info.plist
- Remove taskAffinity from MainActivity (not needed)

Dependencies:
- Add flutter_web_auth_2 for browser-based OAuth
- Remove atproto_oauth_flutter path dependency (to be deleted)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+22 -81
android
app
ios
Runner
lib
+2 -5
android/app/src/main/AndroidManifest.xml
···
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
-
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
···
</activity>
<!-- flutter_web_auth_2 CallbackActivity for OAuth callbacks -->
-
<!-- This MUST be a separate activity to prevent go_router interference -->
+
<!-- Uses private-use URI scheme per atproto spec (RFC 8252) -->
<activity
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
android:exported="true">
···
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
-
-
<!-- OAuth callback URL scheme -->
-
<data android:scheme="dev.workers.brettmay0212.lingering-darkness-50a6"/>
+
<data android:scheme="social.coves"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
+1 -1
ios/Runner/Info.plist
···
<string>social.coves.oauth</string>
<key>CFBundleURLSchemes</key>
<array>
-
<string>dev.workers.brettmay0212.lingering-darkness-50a6</string>
+
<string>social.coves</string>
</array>
</dict>
</array>
+17 -56
lib/config/oauth_config.dart
···
-
import 'package:atproto_oauth_flutter/atproto_oauth_flutter.dart';
-
-
import 'environment_config.dart';
-
-
/// OAuth Configuration for atProto
+
/// OAuth Configuration for Coves Backend OAuth
///
-
/// This configuration provides ClientMetadata for the new
-
/// atproto_oauth_flutter package. The new package handles proper
-
/// decentralized OAuth discovery (works with ANY PDS).
+
/// This configuration supports the backend's mobile OAuth flow.
+
/// The backend handles all OAuth complexity (PKCE, DPoP, token exchange).
+
///
+
/// Uses private-use URI scheme per atproto spec (RFC 8252):
+
/// - Format: social.coves:/callback (single slash!)
+
/// - Works on both Android and iOS without Universal Links complexity
class OAuthConfig {
-
// OAuth Server Configuration
-
// Cloudflare Worker that hosts client-metadata.json and handles OAuth
-
// callbacks
-
static const String oauthServerUrl =
-
'https://lingering-darkness-50a6.brettmay0212.workers.dev';
-
// Custom URL scheme for deep linking
-
// Must match AndroidManifest.xml intent filters
-
// Using the same format as working Expo implementation
-
static const String customScheme =
-
'dev.workers.brettmay0212.lingering-darkness-50a6';
+
// Must match AndroidManifest.xml and Info.plist
+
// Uses reverse domain format per atproto spec
+
static const String customScheme = 'social.coves';
-
// API Configuration
-
// Environment-aware API URL
-
static String get apiUrl => EnvironmentConfig.current.apiUrl;
+
// Redirect URI using private-use URI scheme (RFC 8252)
+
// IMPORTANT: Single slash after scheme per RFC 8252!
+
static const String _redirectUri = '$customScheme:/callback';
-
// Derived OAuth URLs
-
static const String clientId = '$oauthServerUrl/client-metadata.json';
+
/// Get the redirect URI (same for all environments)
+
static String get redirectUri => _redirectUri;
-
// IMPORTANT: Private-use URI schemes (RFC 8252) require SINGLE slash,
-
// not double!
-
// Correct: dev.workers.example:/oauth/callback
-
// Incorrect: dev.workers.example://oauth/callback
-
static const String customSchemeCallback = '$customScheme:/oauth/callback';
-
-
// HTTPS callback (fallback for PDS that don't support custom
-
// URI schemes)
-
static const String httpsCallback = '$oauthServerUrl/oauth/callback';
+
/// Get the callback scheme for FlutterWebAuth2
+
static String get callbackScheme => customScheme;
// OAuth Scopes - recommended scope for atProto
static const String scope = 'atproto transition:generic';
// Client name for display during authorization
static const String clientName = 'Coves';
-
-
/// Create ClientMetadata for the FlutterOAuthClient
-
///
-
/// This configures the OAuth client with:
-
/// - Discoverable client ID (HTTPS URL to metadata JSON)
-
/// - HTTPS callback (primary - works with all PDS servers)
-
/// - Custom URL scheme (fallback - requires PDS support)
-
/// - DPoP enabled for token security
-
/// - Proper scopes for atProto access
-
static ClientMetadata createClientMetadata() {
-
return const ClientMetadata(
-
clientId: clientId,
-
// Use HTTPS as PRIMARY - prevents browser re-navigation that
-
// invalidates auth codes. Custom scheme as fallback (Worker page
-
// redirects to custom scheme anyway)
-
redirectUris: [httpsCallback, customSchemeCallback],
-
scope: scope,
-
clientName: clientName,
-
dpopBoundAccessTokens: true, // Enable DPoP for security
-
applicationType: 'native',
-
grantTypes: ['authorization_code', 'refresh_token'],
-
tokenEndpointAuthMethod: 'none', // Public client (mobile apps)
-
);
-
}
}
+1 -16
pubspec.lock
···
url: "https://pub.dev"
source: hosted
version: "2.13.0"
-
atproto_oauth_flutter:
-
dependency: "direct main"
-
description:
-
path: "packages/atproto_oauth_flutter"
-
relative: true
-
source: path
-
version: "0.1.0"
boolean_selector:
dependency: transitive
description:
···
source: sdk
version: "0.0.0"
flutter_web_auth_2:
-
dependency: transitive
+
dependency: "direct main"
description:
name: flutter_web_auth_2
sha256: "3c14babeaa066c371f3a743f204dd0d348b7d42ffa6fae7a9847a521aff33696"
···
url: "https://pub.dev"
source: hosted
version: "2.1.8"
-
pointycastle:
-
dependency: transitive
-
description:
-
name: pointycastle
-
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
-
url: "https://pub.dev"
-
source: hosted
-
version: "3.9.1"
pool:
dependency: transitive
description:
+1 -3
pubspec.yaml
···
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
-
# New atproto_oauth_flutter package - proper decentralized OAuth implementation
-
atproto_oauth_flutter:
-
path: packages/atproto_oauth_flutter
flutter_secure_storage: ^9.2.2
shared_preferences: ^2.3.3
go_router: ^16.3.0
provider: ^6.1.5+1
flutter_svg: ^2.2.1
dio: ^5.9.0
+
flutter_web_auth_2: ^4.1.0
cached_network_image: ^3.4.1
url_launcher: ^6.3.1
video_player: ^2.8.7 # Pinned for stability