Flutter中使用url_launcher实现OAuth授权并捕获authorization_code问题
问题解决步骤
1. 配置应用 URL Scheme
要让浏览器能跳转回你的应用,必须先在各平台配置对应的 URL Scheme:
iOS 配置(Info.plist)
打开 ios/Runner/Info.plist,添加以下内容:
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>myappurlschemes</string> <!-- 替换成你自己的 Scheme --> </array> </dict> </array>
Android 配置(AndroidManifest.xml)
打开 android/app/src/main/AndroidManifest.xml,在 <activity> 标签内添加 intent-filter:
<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="myappurlschemes" /> <!-- 与 iOS 保持一致的 Scheme --> </intent-filter>
2. 修正授权请求的 redirect_uri
把原来的 redirect_uri 改成完整的 Scheme URI,比如 myappurlschemes://auth/callback,确保授权服务器能正确跳转回应用:
final url = Uri.https('auth_link', '/login', { 'client_id': "clientid", 'redirect_uri': 'myappurlschemes://auth/callback', // 完整的跳转地址 'grant_type': 'authorization_code', 'response_type': 'code', 'code_challenge': codeChallenge, 'code_challenge_method': 'S256', 'scope': 'openid profile offline_access email', // 用空格分隔 scope,部分服务器不支持+ });
3. 监听深链接捕获 authorization_code
使用 uni_links 包(非 OAuth 相关,仅用于处理深链接)监听应用被唤起时的链接,从中提取授权码:
添加依赖
在 pubspec.yaml 中添加:
dependencies: url_launcher: ^6.2.4 uni_links: ^0.5.1
完整实现代码
import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:uni_links/uni_links.dart'; class OAuthLoginPage extends StatefulWidget { const OAuthLoginPage({super.key}); @override State<OAuthLoginPage> createState() => _OAuthLoginPageState(); } class _OAuthLoginPageState extends State<OAuthLoginPage> with WidgetsBindingObserver { StreamSubscription? _linkSubscription; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _setupLinkListener(); } @override void dispose() { _linkSubscription?.cancel(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } // 初始化链接监听,处理冷启动和热启动的跳转 Future<void> _setupLinkListener() async { // 处理应用未启动时的跳转链接 final initialLink = await getInitialLink(); if (initialLink != null) { _extractAuthCode(initialLink); } // 监听应用后台时的跳转链接 _linkSubscription = linkStream.listen((link) { if (link != null) { _extractAuthCode(link); } }); } // 从跳转链接中提取 authorization_code void _extractAuthCode(String link) { final uri = Uri.parse(link); final authCode = uri.queryParameters['code']; if (authCode != null) { // 拿到授权码后,执行后续逻辑(比如换取 token) print('获取到授权码:$authCode'); // 这里可以添加跳转、存储等操作 } } // 启动授权页面 Future<void> _launchAuthPage() async { final codeChallenge = 'your_generated_code_challenge'; // 替换为实际生成的 code challenge final authUrl = Uri.https('auth_link', '/login', { 'client_id': "clientid", 'redirect_uri': 'myappurlschemes://auth/callback', 'grant_type': 'authorization_code', 'response_type': 'code', 'code_challenge': codeChallenge, 'code_challenge_method': 'S256', 'scope': 'openid profile offline_access email', }); if (await canLaunchUrl(authUrl)) { // 使用默认浏览器打开授权页面 await launchUrl(authUrl, mode: LaunchMode.externalApplication); } else { throw '无法打开授权链接'; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('OAuth 登录')), body: Center( child: ElevatedButton( onPressed: _launchAuthPage, child: const Text('开始授权登录'), ), ), ); } }
关键说明
- 确保 URL Scheme 在 iOS 和 Android 配置中完全一致,否则无法唤起应用
redirect_uri必须是授权服务器已配置允许的地址,否则会被拒绝跳转- 使用
LaunchMode.externalApplication强制调用系统默认浏览器,符合你的需求 uni_links仅用于监听深链接,不属于 OAuth 相关包,满足你不想用 OAuth 包的要求
内容的提问来源于stack exchange,提问作者VolkanUstekidag
相关产品推荐
相关产品推荐

