Flutter Web端对接Tesla API:如何拦截OAuth2重定向URI?
使用flutter_web_auth_2实现Tesla OAuth2登录的问题
我正在用flutter_web_auth_2开发Web应用,通过OAuth2访问Tesla账户,当前运行在Web环境。Tesla的重定向URI固定为https://auth.tesla.com/void/callback。调用authenticate方法能打开Tesla授权端点的新标签页完成登录,但应用无法获取重定向URL,反而跳转到Tesla的404页面。
flutter_web_auth_2文档提到需要创建一个端点页面,将重定向信息传递给应用,但我不确定能否在这个固定地址下设置端点?如果不可行,该如何获取重定向结果?其他支持第三方OAuth2登录的网站是如何拦截redirect_uri的?它们是否可以在自身域名内指定自定义URI?
补充说明
附上代码参考,我是Flutter/Dart新手,代码可能不够规范,欢迎指正。打算先在Android移动端测试认证,再迁移到Web应用。
1. 抽象类WebAuthService
abstract class WebAuthService { final String authUrl; final String urlScheme; WebAuthService(this.authUrl, this.urlScheme); Future<String> authenticate(); }
2. TeslaAuth实现类
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 ""; } } }
3. TeslaAuthRequestHelper类
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); // 创建融合utf8和base64url的编解码器,先将字符串编码为utf-8,再编码为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, }); } }
4. 主Widget代码
final authService = TeslaAuthService(); final auth2 = TeslaAuth(); void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // 应用根Widget @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')) ], ), ), ); } }
5. Android Manifest配置
<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"> <!-- 指定Android进程启动后立即应用的主题,Flutter UI初始化时对用户可见,之后用于确定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> <!-- 不要删除下面的meta-data,Flutter工具用它生成GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2" /> </application> </manifest>
在Pixel 4a上测试时,登录后WebView无法返回应用,显示404页面,地址栏中可见重定向URL。已按照flutter_web_auth_2文档建议,在Manifest文件中添加了额外的activity标签。
内容的提问来源于stack exchange,提问作者Kartik Aiyer
相关产品推荐
相关产品推荐

