Flutter前端对接Azure App Services:如何通过Easy Auth实现Azure AD B2C认证?
绝对有可行方案!我之前帮好几个Flutter+Azure项目搞定过类似的认证需求,虽然没有官方适配Easy Auth的Flutter SDK,但咱们可以利用标准协议和Flutter生态里的工具来实现,下面是几个经过验证的方案:
方案1:用Flutter OAuth2/OIDC包直接对接Azure AD B2C(推荐)
Azure App Services的Easy Auth本质是基于标准的OAuth2/OpenID Connect协议,所以我们可以跳过前端直接调用Easy Auth的步骤,让Flutter直接和Azure AD B2C交互获取token,再带着token调用后端API——Easy Auth会自动验证这个token的有效性,完全不用后端写额外的认证代码。
推荐使用flutter_appauth包,它支持移动端安全的授权码+PKCE流程,完美适配Azure AD B2C:
步骤示例
- 在Azure AD B2C中创建一个「原生」类型的应用注册,设置重定向URI为
com.your.app.package://callback(替换成你的Flutter包名)。 - 在Flutter项目中添加依赖:
dependencies: flutter_appauth: ^6.1.1 flutter_secure_storage: ^9.0.0 http: ^1.1.0 - 编写认证和API调用代码:
import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart' as http; final FlutterAppAuth _appAuth = FlutterAppAuth(); final FlutterSecureStorage _secureStorage = FlutterSecureStorage(); // Azure AD B2C配置参数 const String _tenantId = "your-tenant-id"; const String _policyName = "B2C_1_signupsignin"; // 你的用户流名称 const String _clientId = "your-native-app-client-id"; const String _redirectUri = "com.your.app.package://callback"; const List<String> _scopes = [ "openid", "offline_access", "https://$_tenantId.onmicrosoft.com/your-api/api.read" // 对应后端API的scope ]; final String _authority = "https://$_tenantId.b2clogin.com/$_tenantId.onmicrosoft.com/$_policyName"; // 发起登录请求 Future<void> signIn() async { try { final result = await _appAuth.authorizeAndExchangeCode( AuthorizationTokenRequest( _clientId, _redirectUri, authority: _authority, scopes: _scopes, pkceEnabled: true, // 强制开启PKCE,移动端安全必备 ), ); if (result != null) { // 将token存入安全存储,避免明文泄露 await _secureStorage.write(key: "access_token", value: result.accessToken); await _secureStorage.write(key: "refresh_token", value: result.refreshToken); } } catch (e) { print("登录失败: $e"); } } // 调用受保护的Azure App Services API Future<void> callProtectedApi() async { final accessToken = await _secureStorage.read(key: "access_token"); if (accessToken == null) return; final response = await http.get( Uri.parse("https://your-app-service.azurewebsites.net/api/your-endpoint"), headers: {"Authorization": "Bearer $accessToken"}, ); if (response.statusCode == 200) { // 处理成功响应 print("API响应: ${response.body}"); } else if (response.statusCode == 401) { // token过期,尝试刷新token await refreshToken(); // 刷新成功后重新调用API await callProtectedApi(); } } // 刷新token逻辑 Future<void> refreshToken() async { final refreshToken = await _secureStorage.read(key: "refresh_token"); if (refreshToken == null) return; try { final result = await _appAuth.token( TokenRequest( _clientId, _redirectUri, refreshToken: refreshToken, authority: _authority, scopes: _scopes, ), ); if (result != null) { await _secureStorage.write(key: "access_token", value: result.accessToken); if (result.refreshToken != null) { await _secureStorage.write(key: "refresh_token", value: result.refreshToken); } } } catch (e) { print("刷新token失败,需要重新登录: $e"); await signIn(); } }
方案2:用WebView复用Easy Auth登录页面
如果你需要完全复用Azure App Services Easy Auth的自定义登录UI(比如已经配置了品牌化登录页),可以用webview_flutter包模拟浏览器登录流程:
步骤示例
- 在Azure App Services的Easy Auth配置中,添加你的Flutter应用自定义回调URL(比如
com.your.app://auth-complete)到「允许的外部重定向URL」列表。 - 编写WebView登录页面:
import 'package:webview_flutter/webview_flutter.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; final FlutterSecureStorage _secureStorage = FlutterSecureStorage(); class AuthWebView extends StatefulWidget { const AuthWebView({super.key}); @override State<AuthWebView> createState() => _AuthWebViewState(); } class _AuthWebViewState extends State<AuthWebView> { late WebViewController _webViewController; @override Widget build(BuildContext context) { return WebView( initialUrl: "https://your-app-service.azurewebsites.net/.auth/login/aadb2c", javascriptMode: JavascriptMode.unrestricted, navigationDelegate: (NavigationRequest request) { // 检测是否跳转到自定义回调URL if (request.url.startsWith("com.your.app://auth-complete")) { final uri = Uri.parse(request.url); final idToken = uri.queryParameters["id_token"]; final accessToken = uri.queryParameters["access_token"]; if (idToken != null && accessToken != null) { // 保存token到安全存储 _secureStorage.write(key: "id_token", value: idToken); _secureStorage.write(key: "access_token", value: accessToken); // 关闭WebView返回主页面 Navigator.pop(context); } return NavigationDecision.prevent; } return NavigationDecision.navigate; }, onWebViewCreated: (controller) => _webViewController = controller, ); } }
关键注意事项
- 安全优先:移动端必须使用PKCE流程(方案1默认支持),绝对不要用隐式授权流程,避免token泄露。
- token存储:一定要用
flutter_secure_storage这类系统级安全存储工具,不要把token存在SharedPreferences或明文文件里。 - 配置一致性:确保Azure AD B2C应用注册、Easy Auth配置中的租户ID、客户端ID、scope完全匹配,否则会出现认证失败。
- 异常处理:记得处理token过期、网络异常等场景,提升用户体验。
内容的提问来源于stack exchange,提问作者Jeremy W
相关产品推荐
相关产品推荐

