Flutter Web身份验证:如何拦截重定向URI(Tesla API)

iecba09b  于 2023-08-07  发布在  Flutter
关注(0)|答案(1)|浏览(200)

我正在使用flutter_web_auth_2构建一个Web应用程序,该应用程序将使用Oauth2访问我的Tesla帐户。我把这个放到网上。
Tesla的固定重定向URI为https://auth.tesla.com/void/callback。我可以使用authenticate方法,它在我使用凭据登录的tesla授权端点打开一个新选项卡。然而,我的应用程序没有得到重定向URL,而是得到了一个没有从特斯拉找到的页面。
flutter_web_auth_2文档提到了创建一个端点,并在页面上发布重定向到我的应用程序,但我不确定如何在我的应用程序中的这个固定地址(https://auth.tesla.com/void/callback)设置一个端点。这可能吗
如果没有,我如何获得重定向结果?其他允许第三方应用使用OAuth2登录用户并拦截redirect_uri的网站如何做到这一点?他们是否可以在其应用程序域中指定自定义URI?
如果你能帮忙的话,我将不胜感激。

    • 我把代码贴在这里供你参考。我是一个flutter / dart noob,所以请原谅我不整洁或不成熟的代码。如果你看到任何东西,我会很感激任何输入。我想我应该尝试在移动的应用程序(如Android)中执行身份验证,看看在尝试Web应用程序之前是否能找到一些成功。

我创建了一个抽象类,它基本上有一个authenticate方法(实现调用flutter_web_auth2

abstract class WebAuthService {
  final String authUrl;
  final String urlScheme;

  WebAuthService(this.authUrl, this.urlScheme);

  Future<String> authenticate();
}

字符串
上述服务的实现是

class TeslaAuth extends WebAuthService
{
  TeslaAuth() : super("https://auth.tesla.com/oauth2/v3/authorize", "https");

  @override
  Future<String> authenticate() async {
    var request = TeslaAuthRequestHelper().asRequest();
    var response = await http.Client().get(request);
    if(response.statusCode == 200) {
      return response.body;
    } else {
      return "";
    }
  }
}


这使用了一个TeslaAuthRequestHelper,它根据非官方文档创建请求(步骤1)

class TeslaAuthRequestHelper
{

  final clientId = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384";
  final clientSecret = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3";
  late final String codeVerifier;
  late final String codeChallenge;
  final codeChallengeMethod = "S256";
  final redirectUri = "https://auth.tesla.com/void/callback";
  final responseType = "code";
  final scope = "openid email offline_access";
  final state = utf8.fuse(base64Url).encode(getRandomString(20));

  static const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
  static final Random _rnd = Random();

  static String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
      length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
  TeslaAuthRequestHelper() {
    codeVerifier = getRandomString(87);
    //Creates a codec fused with a ut8f and then a base64url codec.
    //The encode on the resulting codec will first encode the string to utf-8
    //and then to base64Url
    codeChallenge = utf8.fuse(base64Url).encode(codeVerifier);
  }

  Uri asRequest() {
    return Uri.https('auth.tesla.com', 'oauth2/v3/authorize', {
      "client_id": "ownerapi",
      // "client_secret": clientSecret,
      "code_challenge": codeChallenge,
      "code_challenge_method": codeChallengeMethod,
      "redirect_uri": redirectUri,
      "response_type": responseType,
      "scope": scope,
      "state": state,
    });
  }
}


最后,我有一个主部件,在中间有一个身份验证按钮,然后尝试显示一个结果,我希望是重定向URL,但我不知道如何获得它。

final authService = TeslaAuthService();
final auth2 = TeslaAuth();

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Tesla Authorizer',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Tesla Web Auth Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _result = '';

  @override
  void initState() {
    super.initState();
    print("Current Uri : ${Uri.base}");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Result: $_result\n'),
            const SizedBox(height: 80),
            ElevatedButton(
                onPressed: () async {
                  _result = await authService.authenticate();
                  print(_result);
                },
                child: const Text('Authenticate'))
          ],
        ),
      ),
    );
  }
}


我在Pixel 4a上尝试了这个,但是我登录了门户网站,但是webview没有返回到我的应用程序,并且我没有找到页面,地址栏中有重定向URL。我按照flutter_web_auth_2文档的建议,在清单文件中添加了一个额外的activity标记。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:label="tesla_auth"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
                android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
                android:exported="true">
            <intent-filter android:label="flutter_web_auth_2">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

ohfgkhjo

ohfgkhjo1#

首先回答你的最后一个问题,是的,其他允许第三方应用使用OAuth2的站点将允许第三方指定一个自定义的重定向URI。
Tesla的API不是官方的,所以除了他们内部使用的/void/callback/ URL之外,没有任何方法可以设置OAuth重定向URL。
经过一番搜索,我找到了https://github.com/carlosonunez/deprecated-tesla-oauth2-token-bot,它表示:
Tesla的Auth API已在/oauth2/v3/authorize和/oauth2/v1/authorize上启用了reCAPTCHA。此外,特斯拉还在一个位置标头中提供了代码,浏览器将自动重定向到该标头。Tesla将在完成此操作后显示404页面。因此,解决此问题的唯一方法是使用Web驱动程序并捕获重定向,这只有在Chrome CDP或面向Web驱动程序的代理服务器的情况下才可能(或有文档记录),这两种情况都是......要获得OAuth令牌需要大量工作。
考虑使用带有嵌入式Web视图的移动的应用程序来满足令牌生成需求。
如果没有代码,很难说在实现中会出现什么问题。
您可以考虑在页面中嵌入一个Web视图,用于跟踪URL并在检测到用户已完成登录(当用户到达/void/callback/页面时)时捕获登录令牌,而不是在新选项卡中打开登录。
不幸的是,Flutter Web对webview/iframe没有太多的支持。https://pub.dev/packages?q=platform%3Aweb+webview显示了几个选项,其中一些选项已存档,而另一些选项则无功能。https://pub.dev/packages/webviewx_plus可能是一个值得首先尝试的好包,因为它似乎有回调,您可以注册它来捕获URL更改。
你正在使用的flutter_web_auth_2插件似乎可以为这个工作。如果你发布了相关的代码,我很乐意看看你是否做错了什么。

相关问题