Oauth2在Flutter app中流动

时间:2017-09-13 11:22:58

标签: oauth oauth-2.0 dart flutter

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

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

我已经完成了一些研究,我得出了以下几个选项:

  • This blog post使用本地服务器,仍需要用户手动关闭窗口,这不是真正的解决方案(在我看来)。
  • This issue标记与所有OAuth提供程序的集成,但未提供有关浏览器内用户身份验证的任何详细信息。
  • This issue与我描述的完全一样,但在底部提到WebView插件提供了一种关闭WebView的方法。虽然它确实有一个close() - 函数,但我找不到在redirect-URI上触发它的方法并返回验证码。

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

提前致谢!

2 个答案:

答案 0 :(得分: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的网址方案。您可以使用平台频道将深层链接参数传递回Dart-land,并在Dart侧的webview上调用close()

答案 1 :(得分:1)

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

特定于Android的代码

首先,创建一个新的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,这不是必需的(只需实现该方法)。

由于频道为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上,在Android上执行相同操作,方法是使用该名称并在同一命令下向下发送URL。然后Dart代码不需要任何更改。

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

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