Skip to content
Closed
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
17 changes: 15 additions & 2 deletions lib/services/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1033,8 +1033,15 @@ class Account extends Service {
final String apiPath = '/account/sessions/oauth2/{provider}'
.replaceAll('{provider}', provider.value);

// Do NOT forward the custom success URL to the server.
// The server only appends key/secret to the redirect when the success path
// matches its internal default (/auth/oauth2/success). Sending a custom URL
// causes the server to skip adding key/secret, so the Flutter webAuth
// callback never receives them → "Invalid OAuth2 Response" (500).
// Let the server use its default success path so key/secret are always
// added. The custom success URL is used only as the callbackUrlScheme so
// Flutter can intercept the redirect after key/secret have been appended.
final Map<String, dynamic> params = {
if (success != null) 'success': success,
if (failure != null) 'failure': failure,
if (scopes != null) 'scopes': scopes,
'project': client.config['project'],
Expand Down Expand Up @@ -1369,8 +1376,14 @@ class Account extends Service {
final String apiPath = '/account/tokens/oauth2/{provider}'
.replaceAll('{provider}', provider.value);

// Do NOT forward the custom success URL to the server.
// The server (account.php) only appends userId/secret to the redirect
// when the success path matches its internal default. Sending a custom
// URL causes the server to skip adding them → "Invalid OAuth2 Response"
// (500) or "Invalid success param" (400) if the URL is not registered.
// Let the server use its default path; use the custom success URL only
// as the callbackUrlScheme so Flutter can intercept the redirect.
final Map<String, dynamic> params = {
if (success != null) 'success': success,
if (failure != null) 'failure': failure,
if (scopes != null) 'scopes': scopes,
'project': client.config['project'],
Expand Down
20 changes: 17 additions & 3 deletions lib/src/client_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -479,11 +479,25 @@ class ClientIO extends ClientBase with ClientMixin {

@override
Future webAuth(Uri url, {String? callbackUrlScheme}) {
// Derive the actual URI scheme to listen for.
// If the caller passed a full URL (e.g. "https://myapp.com/callback"),
// extract just the scheme ("https"). Otherwise use it as-is if it looks
// like a bare scheme, or fall back to the default appwrite-callback scheme.
String resolvedScheme = "appwrite-callback-${config['project']!}";
if (callbackUrlScheme != null && _customSchemeAllowed) {
final parsed = Uri.tryParse(callbackUrlScheme);
if (parsed != null && parsed.hasScheme && parsed.host.isNotEmpty) {
// full URL passed — use just the scheme portion
resolvedScheme = parsed.scheme;
} else {
// bare scheme passed (e.g. "myapp") — use as-is
resolvedScheme = callbackUrlScheme;
}
}

return FlutterWebAuth2.authenticate(
url: url.toString(),
callbackUrlScheme: callbackUrlScheme != null && _customSchemeAllowed
? callbackUrlScheme
: "appwrite-callback-${config['project']!}",
callbackUrlScheme: resolvedScheme,
options: const FlutterWebAuth2Options(
useWebview: false,
),
Expand Down
Loading