Flutter中使用oauth2包实现Microsoft登录后无法重定向回应用
看起来你遇到的是OAuth2登录完成后无法回调回Flutter应用的典型问题,这通常和URL Scheme平台配置缺失以及未处理授权回调逻辑有关,我来一步步帮你解决:
1. 先搞定最关键的:平台URL Scheme配置
系统需要知道哪个App应该响应微软的回调URL,所以必须给Android和iOS分别配置对应的URL Scheme,和你的azureRedirectUrl完全对应。
Android端配置
打开android/app/src/main/AndroidManifest.xml,找到主Activity(通常是io.flutter.embedding.android.FlutterActivity),在其内部添加如下intent-filter:
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"> <!-- 建议加上singleTop避免重复启动 --> <!-- 原有配置保持不变 --> <!-- 新增:捕获微软登录回调的URL --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 完全匹配你的azureRedirectUrl:msauth://com.example.oauth_test/ru4om2-------------- --> <data android:scheme="msauth" android:host="com.example.oauth_test" android:path="/ru4om2--------------" /> </intent-filter> </activity>
iOS端配置
打开ios/Runner/Info.plist,添加以下配置(直接插在<dict>根节点内即可):
<!-- 允许App调用msauth协议 --> <key>LSApplicationQueriesSchemes</key> <array> <string>msauth</string> </array> <!-- 配置App能响应的URL Scheme --> <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>msauth.com.example.oauth_test</string> <!-- 格式:msauth.你的Bundle ID --> </array> <key>CFBundleURLName</key> <string>com.example.oauth_test</string> <!-- 你的Bundle ID --> <key>CFBundleTypeRole</key> <string>Editor</string> </dict> </array>
⚠️ 额外提醒:必须在Azure Portal的App Registration后台,Authentication页面添加Mobile and desktop applications平台,并把你的azureRedirectUrl(比如msauth://com.example.oauth_test/ru4om2--------------)加入允许的重定向URI列表,否则微软会拒绝回调这个地址!
2. 完善登录回调逻辑(核心步骤)
你现在的代码只打开了浏览器,但没有监听App被唤起时的回调URL,没法把微软返回的授权码传给oauth2包完成Token获取。我们用uni_links包来监听回调:
第一步:添加依赖
在pubspec.yaml里加入:
dependencies: flutter: sdk: flutter oauth2: ^2.0.2 url_launcher: ^6.1.12 uni_links: ^0.5.1 # 新增:用于监听App唤起的URL
执行flutter pub get安装依赖。
第二步:修改你的登录代码
更新State类的逻辑,加入URL监听和回调处理:
import 'package:uni_links/uni_links.dart'; import 'package:oauth2/oauth2.dart' as oauth2; import 'package:url_launcher/url_launcher.dart'; // 你的State类部分 class _YourLoginPageState extends State<YourLoginPage> { String _status = 'Ready to Login'; oauth2.AuthorizationCodeGrant? _grant; StreamSubscription? _uriSubscription; // 常量直接引用你定义的 static const String clientId = azureClientId; static const String redirectUrl = azureRedirectUrl; static const String authorizationEndpoint = azureAuthorizationEndpoint; static const String tokenEndpoint = azureTokenEndpoint; static const List<String> scopes = azureOutlookScopes; @override void initState() { super.initState(); // 初始化URL监听:处理冷启动/热启动的回调 _initUriListener(); } @override void dispose() { _uriSubscription?.cancel(); _grant?.close(); super.dispose(); } // 初始化URL监听 Future<void> _initUriListener() async { // 处理App未启动时被唤起的情况(冷启动) final initialUri = await getInitialUri(); if (initialUri != null) { _handleAuthCallback(initialUri); } // 处理App在后台时被唤起的情况(热启动) _uriSubscription = uriLinkStream.listen( _handleAuthCallback, onError: (err) => debugPrint('URL监听错误: $err'), ); } // 处理登录回调的URL,完成Token获取 Future<void> _handleAuthCallback(Uri uri) async { if (_grant == null) { setState(() => _status = '错误:没有活跃的登录会话'); return; } setState(() => _status = '处理登录响应中...'); try { // 用回调URL完成授权,获取Token final client = await _grant!.handleAuthorizationResponse(uri.queryParameters); debugPrint('登录成功!Token: ${client.credentials.accessToken}'); // 这里可以把Token保存到本地(比如shared_preferences) // 登录成功后的逻辑:比如跳转到首页 setState(() => _status = '登录成功!'); } catch (e) { setState(() => _status = '登录失败: $e'); debugPrint('授权错误: $e'); } } // 你的原有登录方法,做小调整 Future<void> _startLogin() async { try { setState(() => _status = '打开浏览器中...'); // 创建授权实例 _grant = oauth2.AuthorizationCodeGrant( clientId, Uri.parse(authorizationEndpoint), // 必须转成Uri类型 Uri.parse(tokenEndpoint), ); // 生成微软登录URL final authUrl = _grant!.getAuthorizationUrl( Uri.parse(redirectUrl), scopes: scopes, ); // 打开系统默认浏览器 final launched = await launchUrl( authUrl, mode: LaunchMode.externalApplication, // 用外部浏览器打开,确保回调正常 ); if (!launched) { setState(() => _status = '错误:无法打开浏览器'); return; } setState(() => _status = '浏览器已打开,请完成登录...'); } catch (e) { setState(() => _status = '启动登录失败: $e'); debugPrint('登录启动错误: $e'); } } // 你的build方法保持不变... }
3. 最后检查细节
- 确保
azureRedirectUrl和平台配置、Azure后台的重定向URI完全一致,大小写、特殊字符都不能错 - 测试时尽量用真机,部分模拟器的浏览器可能存在回调问题
- 登录后如果还是有问题,查看
debugPrint的日志,重点看是否有回调URL被捕获、是否有Azure返回的错误信息
按照上面的步骤配置和修改后,应该就能正常完成登录并回调回App了,有问题可以再查日志找具体原因~ 😊




