Flutter微软身份验证插件msal_flutter_tlmn的使用
Flutter 微软身份验证插件 msal_flutter_tlmn 的使用
版本 3.0.0
我们已经将代码库更新到 Dart 3,并更改了 iOS 刷新令牌处理。从原始库分叉并将其版本号更改为 3.0.0。当前使用的分支为 msal_flutter,但目前我们不使用它。
升级指南
要将旧项目升级到 V2,请确保执行以下操作:
- 更新 Gradle 至 3.6.0+
- 更新 Kotlin 至 1.4.31 或更高版本(较低版本可能可以工作,但未经过测试)
- 更新
msal_default_config.json
文件至此仓库中的最新版本 - 更新 Flutter 至最新版本
版本 1.0.0+ 警告
版本 1.0.0 使用更新后的 MSAL 库并迁移到 Android-X。1.0.0 不兼容旧版本。请仅在准备好迁移您的 Android 应用并更改构造函数调用方式时才更新到 1.0.+。版本 1+ 是在 iOS 13+ 上使用 MSAL 所必需的。
建议不要使用 login.microsoftonline.com
权限和端点,因为它们似乎正在被弃用且由于域相同导致无法区分保存的密码。
新的权限模板是:
"https://<tenant>.b2clogin.com/tfp/<tenant>.onmicrosoft.com/<user-flow>"
例如:
"https://msalfluttertest.b2clogin.com/tfp/msalfluttertest.onmicrosoft.com/B2C_1_sisu"
对于新构建中已知问题的排查,请滚动到底部,所有找到的错误和修复将在那里列出。
MSAL 包装库用于 Flutter
请注意,该产品处于非常早期的 alpha 阶段,可能会发生变化并且存在错误。
Microsoft Authentication Library Flutter Wrapper 是一个使用 MSAL 库为 Android 和 iOS 提供公共客户端应用功能的包装器。目前仅支持隐式工作流。
如果您有其他功能需求,请告知我。
设置
要使用 MSAL Flutter 在您的库中,请首先设置 Azure AD B2C 租户和移动客户端(如果尚未完成),详细的说明可以在以下链接中找到: https://docs.microsoft.com/en-us/azure/active-directory-b2c/
Flutter
在您的 Flutter 应用程序中导入 Msal Flutter
包,请将其添加到 pubspec.yaml
文件的依赖项列表中。
dependencies:
msal_flutter: ^1.0.0+2
Android (Kotlin)
请确保您使用的是 Kotlin 版本 1.4.31 或更高版本。转到您的应用的 Android 文件夹,打开 build.gradle
文件,在 buildscript:ext.kotlin_version
下更改版本为 1.4.31 或更高版本。
步骤:
-
确保您的
AndroidManifest.xml
包含互联网权限<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
在
AndroidManifest.xml
文件中添加以下意图过滤器,替换占位符<YOUR-CLIENT-ID>
为您 Azure B2C 应用程序的客户端 ID。<activity android:name="com.microsoft.identity.client.BrowserTabActivity"> <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="msauth" android:host="<YOUR_PACKAGE_NAME>" android:path="/<YOUR_BASE64_ENCODED_PACKAGE_SIGNATURE>" /> </intent-filter> </activity>
-
从这个仓库复制
msal_default_config.json
(或者如果您知道怎么做就自己创建一个)并将其放置在您的 Flutter 应用的android/src/main/res/raw
文件夹中。 默认/传统的重定向 URL 为msal<YOUR-CLIENT-ID>://auth
,但如果选择了不同的重定向 URL,请输入该 URL。注意,重定向 URL 方案和主机组合必须对您的应用程序唯一,如果更改了重定向 URL,也必须在步骤 2 中的活动意图过滤器中更改。
警告:不要将应用程序类型设置为单个,MSAL Flutter 包装器仅与新的多账户配置兼容。
- 最低 SDK 版本必须至少为 21。如果您从默认为 16 的新 Flutter 应用开始,请在
android > app > build.gradle
文件中的android:defaultConfig>minSdkVersion
下更改此设置。
iOS (Swift)
此部分主要复制并修改自官方 iOS MSAL 库 GitHub 存储库的第一步。访问存储库以获取更多详细信息。
步骤:
-
将您的回调 URL 方案添加到您的
Info.plist
文件中,替换占位符<YOUR-CLIENT-ID>
为您 Azure B2C 应用程序的客户端 ID。<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>msauth.[BUNDLE-ID]</string> </array> </dict> </array>
-
添加
LSApplicationQueriesSchemes
以便允许调用 Microsoft Authenticator(用于认证代理)<key>LSApplicationQueriesSchemes</key> <array> <string>msauthv2</string> <string>msauthv3</string> </array>
-
打开您的 iOS 项目的 Xcode,点击 Runner 应用以打开配置,然后在能力下扩展 Keychain 共享并添加键链组
com.microsoft.adalcache
-
在您的
AppDelegate.swift
中导入 MSAL 库,通过在文件顶部添加以下内容:import MSAL
-
向您的
AppDelegate
类添加以下函数override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { guard let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String else { return false } return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApplication) }
-
排查问题 可能会遇到一些错误,例如最低 iOS 部署版本太低。MSAL Flutter 需要最低 iOS 版本为 11.0。 要设置此版本,请在
Podfile
文件的第一行添加platform :ios, '11.0'
,该文件位于您的 iOS 文件夹的根目录中。
当从旧版本的 MSAL Flutter 升级时,您可能还需要删除 Podfile.lock
文件,该文件也在 iOS 文件夹中。
如何使用
-
在 Flutter 中导入包
import 'package:msal_flutter/msal_flutter.dart';
-
使用静态工厂方法
createPublicClientApplication
异步创建一个新的对象实例,通过提供您的客户端 ID 和可选的权限。 使用默认权限:var pca = await PublicClientApplication.createPublicClientApplication("YOUR-CLIENT-ID");
指定权限:
var pca = await PublicClientApplication.createPublicClientApplication("YOUR-CLIENT-ID", authority: "https://<tenant>.b2clogin.com/tfp/<tenant>.onmicrosoft.com/<user-flow>");
如果权限为空,则将使用由相关 MSAL 库实现定义的默认权限。
-
若要交互式检索令牌,请调用
acquireToken
函数并传递您希望获取令牌的范围。请注意,此函数会在失败时抛出错误,并应使用 try-catch 块包围,如下面的示例所示 不要包括默认添加的openid
或user_impersonation
范围try{ String token = await pca.acquireToken(["https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation"]); } on MsalException { // 错误处理逻辑 }
-
一旦用户至少登录过一次,可以通过调用
acquireTokenSilent
函数异步检索令牌,传递您希望获取令牌的范围。请注意,此函数会在失败时抛出错误,并应使用 try-catch 块包围,如下面的示例所示 不要包括默认添加的openid
或user_impersonation
范围try{ String token = await pca.acquireTokenSilent(["https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation"]); } on MsalException{ // 错误处理逻辑 }
-
要注销,请调用
logout
方法try{ await pca.logout(); } on MsalException{ // 错误处理逻辑 }
可能抛出的异常列表
异常 | 描述 |
---|---|
MsalException | 基础异常,继承自所有其他异常。用于一般或未知错误 |
MsalChangedClientIdException | 尝试初始化具有不同客户端 ID 的第二个客户端 ID |
MsalInitializationException | 初始化客户端时出错。最可能是由于配置文件不正确 |
MsalInvalidConfigurationException | 在设置公共客户端应用时出现配置错误,例如无效的客户端 ID 或权限 |
MsalInvalidScopeException | 无效的范围或未提供任何范围。目前仅在 Android 中受支持 |
MsalNoAccountException | 用户之前未登录、已注销或刷新令牌已过期,无法执行 acquireTokenSilent |
MsalUninitializedException | 在客户端初始化之前调用了客户端方法 |
MsalUserCancelledException | 用户取消了登录请求。目前仅在 Android 中受支持,对于 iOS 则抛出 MsalException |
故障排除
请注意,目前在使用稍旧版本 Kotlin 的 Android 上似乎存在一个问题。
如果您在尝试获取令牌时遇到类似“找不到静态成员 msalApp”的错误,请转到您的应用的 Android 文件夹,打开 build.gradle
文件,并在第二行将 Kotlin 版本从 1.3.10 更改为 1.3.50。有关更多信息,请参阅问题 #4。
修复将很快实施。
示例代码
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:msal_flutter_tlmn/msal_flutter_tlmn.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static const String _authority =
"https://msalfluttertest.b2clogin.com/tfp/3fab2993-1fec-4a8c-a6d8-2bfea01e64ea/B2C_1_phonesisu";
static const String _iosRedirectUri =
"msauth.com.muljin.msalflutterv2://auth";
static const String _androidRedirectUri =
"msauth://uk.co.moodio.msal_flutter_example/TvkGQnk1ERb%2Bl9pB4OeyeWrYmqo%3D";
static const String _clientId = "fc6136e7-43d1-489c-b221-630e9e4402d3";
static const List<String> _scopes = [
"https://msalfluttertest.onmicrosoft.com/msaltesterapi/All"
];
String _output = 'NONE';
final config = MSALPublicClientApplicationConfig(
androidRedirectUri: _androidRedirectUri,
iosRedirectUri: _iosRedirectUri,
clientId: _clientId,
androidConfig: MSALAndroidConfig(
authorities: [Authority(authorityUrl: Uri.parse(_authority))]),
authority: Uri.parse(_authority),
);
MSALPublicClientApplication? pca;
List<MSALAccount>? accounts;
Future<void> _acquireToken() async {
print("called acquiretoken");
// 创建 PCA 如果尚未创建
if (pca == null) {
print("creating pca...");
pca = await MSALPublicClientApplication.createPublicClientApplication(config);
await pca!.initWebViewParams(MSALWebviewParameters());
}
print("pca created");
String res = '';
try {
MSALResult? resp = await pca!
.acquireToken(MSALInteractiveTokenParameters(scopes: _scopes));
res = resp?.account.identifier ?? 'noAuth';
} on MsalUserCancelledException {
res = "User cancelled";
} on MsalNoAccountException {
res = "no account";
} on MsalInvalidConfigurationException {
res = "invalid config";
} on MsalInvalidScopeException {
res = "Invalid scope";
} on MsalException {
res = "Error getting token. Unspecified reason";
}
setState(() {
_output = res;
});
}
Future<void> _loadAccount() async {
if (pca == null) {
print("initializing pca");
pca = await MSALPublicClientApplication.createPublicClientApplication(config);
await pca!.initWebViewParams(MSALWebviewParameters());
}
try {
final result = await pca!.loadAccounts();
if (result != null) {
accounts = result;
}
} catch (e) {
log(e.toString());
}
setState(() {});
}
Future<void> _acquireTokenSilently() async {
if (pca == null) {
print("initializing pca");
pca = await MSALPublicClientApplication.createPublicClientApplication(config);
await pca!.initWebViewParams(MSALWebviewParameters());
}
String res = 'res';
try {
final response = await pca!.acquireTokenSilent(
MSALSilentTokenParameters(
scopes: _scopes,
),
accounts?.isEmpty == true ? null : accounts?.first);
res = response?.account.identifier ?? '';
} on MsalUserCancelledException {
res = "User cancelled";
} on MsalNoAccountException {
res = "no account";
} on MsalInvalidConfigurationException {
res = "invalid config";
} on MsalInvalidScopeException {
res = "Invalid scope";
} on MsalException {
res = "Error getting token silently!";
}
print("Got token");
print(res);
setState(() {
_output = res;
});
}
Future _logout() async {
print("called logout");
if (pca == null) {
print("initializing pca");
pca = await MSALPublicClientApplication.createPublicClientApplication(config);
await pca!.initWebViewParams(MSALWebviewParameters());
}
print("pca is not null");
String res;
try {
if (accounts?.isNotEmpty == true) {
await pca!.logout(MSALSignoutParameters(), accounts!.first);
}
res = "Account removed";
} on MsalException {
res = "Error signing out";
} on PlatformException catch (e) {
res = "some other exception ${e.toString()}";
}
print("setting state");
setState(() {
_output = res;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: _acquireToken,
child: Text('AcquireToken()'),
),
ElevatedButton(
onPressed: _loadAccount, child: Text('loadAccount()')),
ElevatedButton(
onPressed: _acquireTokenSilently,
child: Text('AcquireTokenSilently()')),
ElevatedButton(onPressed: _logout, child: Text('Logout')),
Text(_output),
Expanded(
child: ListView.builder(
itemCount: accounts?.length ?? 0,
itemBuilder: (context, index) {
final item = accounts![index];
return ListTile(
title: Text(item.username ?? item.identifier),
);
},
))
],
),
),
),
);
}
}
更多关于Flutter微软身份验证插件msal_flutter_tlmn的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter微软身份验证插件msal_flutter_tlmn的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用 msal_flutter_tlmn
插件进行微软身份验证的示例代码。这个插件允许Flutter应用通过Microsoft Authentication Library (MSAL) 进行身份验证。
首先,确保你已经在你的 pubspec.yaml
文件中添加了 msal_flutter_tlmn
依赖:
dependencies:
flutter:
sdk: flutter
msal_flutter_tlmn: ^latest_version # 请替换为最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来是具体的代码示例:
1. 配置 MSAL
在你的 Flutter 应用中,创建一个配置类来存储你的 MSAL 配置信息。这通常包括客户端ID、重定向URI等。
import 'package:msal_flutter_tlmn/msal_flutter_tlmn.dart';
class MSALConfig {
static final String clientId = "your-client-id"; // 替换为你的客户端ID
static final List<String> redirectUris = ["msauth.yourpackage.name://auth"]; // 替换为你的重定向URI
static final String authority = "https://login.microsoftonline.com/your-tenant-id"; // 替换为你的租户ID或组织ID
}
2. 初始化 MSAL
在你的应用启动时,初始化 MSAL。
import 'package:flutter/material.dart';
import 'package:msal_flutter_tlmn/msal_flutter_tlmn.dart';
import 'msal_config.dart'; // 假设你将配置类放在这个文件中
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Microsoft Authentication Example'),
),
body: MSALInitializer(
clientId: MSALConfig.clientId,
redirectUris: MSALConfig.redirectUris,
authority: MSALConfig.authority,
onInitialized: (PublicClientApplication client) {
// 初始化成功后,你可以在这里保存client实例以进行后续操作
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AuthScreen(client: client)),
);
},
onError: (String errorMessage, Exception exception) {
// 处理初始化错误
print("MSAL Initialization Error: $errorMessage");
},
),
),
);
}
}
3. 执行身份验证
在你的 AuthScreen
中,添加一个按钮来触发身份验证流程。
import 'package:flutter/material.dart';
import 'package:msal_flutter_tlmn/msal_flutter_tlmn.dart';
class AuthScreen extends StatefulWidget {
final PublicClientApplication client;
AuthScreen({required this.client});
@override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
late String resultText = "Not Authenticated";
void _authenticate() async {
try {
var result = await widget.client.acquireTokenInteractive(
scopes: ["User.Read"], // 请求的权限范围
);
setState(() {
resultText = "Authenticated! Access Token: ${result.accessToken}";
});
} catch (e) {
setState(() {
resultText = "Authentication Failed: ${e.message}";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Authenticate with Microsoft'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(resultText),
SizedBox(height: 20),
ElevatedButton(
onPressed: _authenticate,
child: Text('Authenticate'),
),
],
),
),
);
}
}
4. 处理重定向URI
在你的 AndroidManifest.xml
和 Info.plist
中添加相应的配置来处理重定向URI。
Android (AndroidManifest.xml
)
<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="msauth.yourpackage.name" android:host="auth" />
</intent-filter>
iOS (Info.plist
)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.yourpackage.name</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
<string>msauthv3</string>
</array>
以上代码展示了如何使用 msal_flutter_tlmn
插件在Flutter应用中实现微软身份验证。请确保替换示例代码中的占位符(如 your-client-id
和 your-tenant-id
)为你的实际值。