flutter_web_auth_2 icon indicating copy to clipboard operation
flutter_web_auth_2 copied to clipboard

Custom callback schemes for desktop using deep links

Open Robert-Stackflow opened this issue 6 months ago • 2 comments

The package protocol_handler is used to register and handle custom protocols, which also supports desktop platforms.So I thought I could implement the redirect callback through this plugin.The example code is as follows:

import 'dart:async';

import 'package:flutter_web_auth_2_platform_interface/flutter_web_auth_2_platform_interface.dart';
import 'package:protocol_handler/protocol_handler.dart';
import 'package:url_launcher/url_launcher.dart';

/// Implements the plugin interface using an internal server
class AuthProtocolListener implements ProtocolListener {
  /// Creates a new instance of the [AuthProtocolListener]
  const AuthProtocolListener(this.onReceivedUrl);

  /// Callback for when a URL is received
  final Function(String) onReceivedUrl;

  @override
  Future<void> onProtocolUrlReceived(String url) async {
    onReceivedUrl.call(url);
  }
}

/// Implements the plugin interface using an internal server (currently used by
/// Windows and Linux).
class FlutterWebAuth2ProtocolPlugin extends FlutterWebAuth2Platform {
  /// Registers the server implementation.
  FlutterWebAuth2ProtocolPlugin() {
    _protocolListener = AuthProtocolListener((url) async {
      if (url.contains(_callbackUrl)) {
        _handleUriCallback(url);
        protocolHandler.removeListener(_protocolListener);
      }
    });
  }

  ///[optional] CallbackUrl is used to match whether the received URI
  ///matches the redirect address
  String _callbackUrl = '';
  late AuthProtocolListener _protocolListener;

  Completer<String>? _completer;

  void _handleUriCallback(String uri) {
    if (_completer != null && !_completer!.isCompleted) {
      _completer!.complete(uri);
    }
  }

  @override
  Future<String> authenticate({
    required String url,
    required String callbackUrl,
    required String callbackUrlScheme,
    required Map<String, dynamic> options,
  }) async {
    _callbackUrl = callbackUrl;
    protocolHandler.addListener(_protocolListener);
    ///Block the thread until the _handleUriCallback method is actively called
    _completer = Completer<String>();

    await launchUrl(Uri.parse(url));

    return _completer!.future;
  }

  @override
  Future clearAllDanglingCalls() async {
    ///If the user cancels the authentication process, the _completer will
    ///never be completed. This will cause the thread to hang indefinitely.
    ///This method is used to clear all dangling calls.
    _handleUriCallback('');
  }
}
  1. Before performing this operation, developers need to modify their code according to protocol_handler to support the application receiving the URI.At the same time, developers also need to pay attention to registering the same schemes as the parameter callbackUrlScheme of the authenticate function.
  2. To avoid receiving an incorrect callback URI, you can match the received URI by specifying the callbackUrl.
  3. By using custom callback schemes, developers can avoid security risks caused by loopback addresses.For example, Google has disabled the use of loopback addresses as redirect url.
  4. Compared with the method using Webview, the oauth flow of this method is more similar with the oauth flow of other native Windows applications. Moreover, when I use Webview, I will encounter the problem of application crashing.

Robert-Stackflow avatar Aug 21 '24 16:08 Robert-Stackflow