首页 文章

Oauth2在Flutter app中流动

提问于
浏览
4

在我正在构建的Flutter应用程序中,我需要根据自定义(非Google / Facebook / Twitter / etc)授权服务器对用户进行身份验证 .

为了实现这一点,用户应在网页中填写凭据 . 为此,可以使用WebView-plugin . 但是,在用户进行身份验证后重定向页面时,应关闭WebView,并将代码传递给最初调用WebView的(Flutter)函数 .

我已经做了一些研究,我得到了以下几个选项:

  • This blog post使用本地服务器,仍然需要用户手动关闭窗口,这不是一个真正的解决方案(在我看来) .

  • This issue标记与任何OAuth提供程序的集成已完成,但未提供有关浏览器内用户身份验证的任何详细信息 .

  • This issue与我描述的完全一样,但在底部提到WebView插件提供了一种关闭WebView的方法 . 虽然它确实有close()函数,但我找不到在redirect-URI上触发它并返回验证码的方法 .

是否存在解决方案,一旦打开redirect-URI,它会自动关闭浏览器(并返回验证码)?

提前致谢!

2 回答

  • 2

    我没试过这个,但我的想法是使用 FlutterWebviewPlugin 将用户发送到像 https://www.facebook.com/v2.8/dialog/oauth?client_id={app-id}&redirect_uri=fbAPP_ID://authorize 这样的网址 . 然后为 application:openURL:options: (在iOS上)和 onNewIntent (Android)添加本机处理程序,并修改 AndroidManifest.xmlInfo.plist 以注册应用程序以接收来自 fbAPP_ID 方案的URL . 您可以使用平台通道将深层链接参数传递回Dart-land,并在Dart侧的webview上调用 close() .

  • 0

    根据@Igor的要求,我将发布用于解决此问题的代码 . 这个想法既基于@CollinJackson的答案,也基于AppAuth库如何做同样的事情 . 注意:我这里没有iOS代码,但对于经常进行iOS开发的人来说代码应该是非常简单的 .

    Android-specific code

    首先,创建一个新的Activity,并在清单中注册它以接收URI:

    <activity
            android:name=".UriReceiverActivity"
            android:parentActivityName=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="organization" android:host="login.oauth2" /> 
                <!-- Triggering URI would be organization://login.oauth2 -->
            </intent-filter>
        </activity>
    

    在Java代码中,默认情况下,有一个Activity( MainActivity ) . 在此活动中开始新的 MethodChannel

    public class MainActivity extends FlutterActivity implements MethodChannel.MethodCallHandler {
    
      private static final String CHANNEL_NAME = "organization/oauth2";
    
      public static MethodChannel channel;
    
      @Override  
      protected void onCreate(Bundle savedInstanceState) {    
          super.onCreate(savedInstanceState);    
          GeneratedPluginRegistrant.registerWith(this);
    
          channel = new MethodChannel(getFlutterView(), CHANNEL_NAME);
          channel.setMethodCallHandler(this);
      }
    }
    

    请注意,此代码不完整,因为我们还处理来自 this 的调用 . 刚刚实现了这个方法,你可以添加方法调用 . 例如,我们使用此渠道启动Chrome自定义标签 . 但是,要将密钥返回Dart-land,这不是必需的(只需实现该方法) .

    由于 Channels 是 public ,我们可以在 UriReceiverActivity 中调用它:

    public class UriReceiverActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Uri data = getIntent().getData();
        Map<String, Object> map = new HashMap<>();
        map.put("URL", data.toString());
        MainActivity.channel.invokeMethod("onURL", map);
    
        // Now that all data has been sent back to Dart-land, we should re-open the Flutter
        // activity. Due to the manifest-setting of the MainActivity ("singleTop), only a single
        // instance will exist, popping the old one back up and destroying the preceding
        // activities on the backstack, such as the custom tab.
        // Flags taken from how the AppAuth-library accomplishes the same thing
        Intent mainIntent = new Intent(this, MainActivity.class);
        mainIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(mainIntent);
        finish();
    }
    }
    

    这很大程度上受到了this代码的启发 .

    现在,重新打开Flutter应用程序,并将URL(带有令牌)发送回Dart-land .

    Flutter code

    在Dart中,我们在通道上有一个单独的监听器(我只会发布代码的片段,因为它不是很好并且散布在文件周围):

    // Member declaration
      _platform = const MethodChannel('organization/oauth2');
      // Instantiation in constructor
      _platform.setMethodCallHandler(_handleMessages);
      // Actual message handler:
      void _handleMessages(MethodCall call) {
        switch (call.method) {
          case "onURL":
            // Do something nice using call.arguments["URL"]
        }
      }
    

    在iOS上,通过使用该名称并在同一命令下沿着通道发送URL,在Android上执行相同操作 . 然后Dart代码不需要任何更改 .

    至于启动浏览器,我们只使用url_launcher插件 . 请注意,我们不限于使用WebView,我们可以使用设备上的任何浏览器 .

    请注意,可能有更简单的方法来实现这一点,但由于我们必须在Flutter的alpha中尽早完成,我们无法查看其他实现 . 我应该在某个阶段简化它,但我们还没有找到时间 .

相关问题