Flutter密码管理插件passkeys_capsule的使用
Flutter密码管理插件passkeys_capsule的使用

passkeys
一个用于通过passkeys(基于WebAuthn/FIDO2)进行身份验证的Flutter包。
请访问https://passkeys.flutter.corbado.io查看一个在线演示(这是Vercel上的一个Flutter Web部署示例)。
Android | iOS | Linux | macOS | Web | Windows | |
---|---|---|---|---|---|---|
支持 | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
特性
- 在iOS、Android和Web上通过passkeys注册和登录用户。
- 通过条件UI登录用户。
入门指南
了解如何在Flutter应用中设置passkeys的最佳方式是阅读以下简介,然后查看此包的示例。
简介
当涉及到理解passkeys时,有三个参与方:
- 您的Flutter应用的用户
- 您的Flutter应用(安装在具有Face ID或Touch ID等设备上的设备)
- 依赖方服务器(用户的公钥将存储在后端)
更多信息
与传统的基于密码的身份验证流程类似,用户首先必须注册,即设置一个passkey。该流程如以下图像所示。
首先,用户将在您的应用中提供其电子邮件地址或用户名。此电子邮件地址将发送到依赖方服务器。您可以自行实现该服务器,也可以依赖于预构建的服务器(例如由Corbado提供的)。
依赖方服务器的响应将包含publicKeyCredentialCreationOptions
。它包含了用户设备设置passkey所需的所有信息。
您的应用现在会与本机设备的操作系统交互(无需担心,实际工作已由该Flutter包抽象化),以请求用户设置一个passkey。他只需要提供一次生物特征信息(例如通过Face ID或Touch ID)。在此设置之后,会创建一个私钥和公钥。私钥安全地存储在用户的设备上。公钥被发送到依赖方服务器。为了使这有效,应用必须与依赖方服务器关联。这涉及一些配置在您的应用和依赖方服务器上(有关详细信息,请参阅我们解释如何设置示例的部分)。 依赖方服务器将验证并存储公钥。之后,它将以成功消息(例如JWT令牌)响应。
从那时起,用户可以使用这些生物识别信息登录您的应用。每次登录时,您的应用都会向依赖方服务器请求一个挑战
。依赖方服务器生成一个必须用私钥签名的挑战。为了访问该私钥,应用再次请求用户输入其生物特征(对本机设备操作系统发出另一次调用)。在用户提供了生物特征(例如通过指纹读取器)之后,挑战会被签名,并且签名后的挑战被发送到依赖方服务器。使用公钥,服务器将验证签名后的挑战,并以成功消息(例如JWT令牌)响应。
示例
要运行示例,请遵循以下步骤:
- 克隆GitHub仓库
git clone git@github.com:corbado/flutter-passkeys.git
- 如果您想在iOS模拟器或Android模拟器上运行示例,请现在启动一个(如果您想在浏览器中启动示例,则跳过此步骤)。
- 运行
melos run example-passkeys-native
以在iOS/Android上启动示例。 - 提供电子邮件地址并点击“注册”来注册新用户。
- 登出后,您可以使用相同的电子邮件地址再次登录,或者提供一个新的电子邮件地址来注册另一个用户。
有关更多说明和故障排除,请参阅示例的README。
使用方法
要了解如何使用此包来注册和登录用户,请看下面的代码片段。它是从corbado_auth提取的。我们在那里使用它来构建一个基于passkey的身份验证SDK。要查看此代码与UI的连接,请查看示例并在您的机器上运行它。提示:您还可以调试它以更好地理解其工作原理。
_register() async {
final passkeyAuthenticator = PasskeyAuthenticator();
final relyingPartyServer = RelyingPartyServer(); // 这不是此包的一部分
// 通过调用依赖方服务器开始注册
final webAuthnChallenge = await relyingPartyServer.startSignUpWithPasskey(
'user@example.com', 'Username');
// 调用平台认证器在设备上注册新的passkey
final platformRes = await passkeyAuthenticator.register(webAuthnChallenge);
// 再次调用依赖方服务器完成注册
final relyingPartyServerRes = await relyingPartyServer.finishSignUpWithPasskey(platformRes);
}
_signIn() async {
final passkeyAuthenticator = PasskeyAuthenticator();
final relyingPartyServer = RelyingPartyServer(); // 这不是此包的一部分
// 通过调用依赖方服务器开始登录
final webAuthnChallenge = await relyingPartyServer.startLoginWithPasskey('user@example.com');
// 调用平台认证器请求用户使用passkey签署挑战
final platformRes = await passkeyAuthenticator.authenticate(webAuthnChallenge);
// 再次调用依赖方服务器完成登录
final relyingPartyServerRes = await relyingPartyServer.finishLoginWithPasskey(platformRes);
}
我们现在将快速浏览一下Flutter开发者在使用passkeys时最常见的几个用例。我们将尝试展示此包如何帮助解决每个用例。虽然用例1非常基础(仅用于探索),但用例2、3和4展示了真实应用的案例。
1. 用例:您只想使用passkeys进行原型设计
您只是想在Flutter应用中看到passkeys的实际效果吗?那么此包的示例就是您开始的好地方。无需任何配置,您可以在模拟器中体验注册和登录流程。
注意:
- ⚠️ 您与其他Flutter开发者共享一个依赖方服务器。它的用户表每天都会被清空。我们这样构建示例是为了使其非常简单地设置。对于示例来说,这完全没问题,但是如果您想构建自己的应用,则需要自己的依赖方服务器。
- ⚠️ 您不能在物理iOS设备上运行示例,只能在模拟器上运行(对于Android,物理设备和模拟器都适用)。
2. 用例:您希望为您的应用使用passkeys并且已经构建了自己的依赖方服务器
如果您已经有一个实现了WebAuthn标准的依赖方服务器,您可以使用此包来抽象出使用passkeys所需的特定平台代码。查看上面“使用方法”部分中的代码,大致了解如何将您的依赖方服务器与此包对接。
3. 用例:您希望为您的应用使用passkeys但不希望自己构建依赖方服务器
要在您的应用中使用passkeys,您需要一个依赖方服务器。如果您不想自己构建,可以使用现有的解决方案。为了节省时间和精力,您可以使用Corbado作为依赖方服务器。如果想要试用,请使用corbado_auth。在这里找到包括逐步指南在内的示例。
FYI: 您可以使用两种变体:
- 您正在构建一个新的应用且没有现有用户群:在这种情况下,您可以使用来自corbado_auth的CorbadoAuth,并让它为您管理passkeys和用户。
- 您希望将passkeys引入已有用户的应用:在这种情况下,您可以使用来自corbado_auth的CustomCorbadoAuth,并让它为您管理passkeys。不过,您仍将继续管理用户和会话。
4. 用例:您希望与Firebase一起使用passkeys
Firebase是一个可以帮助您构建应用的伟大技术。要与Firebase一起使用passkeys,您可以使用corbado_auth_firebase。
故障排除
Android
1. 确保已登录Google账户
Google备份了所有的passkeys,因此您需要登录到Google账户才能使用passkeys。如果您未登录,您将看到以下消息:“登录到您的Google账户以创建passkeys。” 此包将抛出一个SyncAccountNotAvailableException
。
您可以通过打开设置,点击右上角的图标,然后点击“登录到您的Google账户”来登录您的Google账户。

2. 确保已在设备上设置屏幕锁或生物特征
如果在模拟器中运行应用程序时提示无法创建passkeys,请登录到您的Google账户并正确设置屏幕锁或生物特征。
要设置屏幕锁,请打开设置,搜索安全设置并添加一个PIN码以及指纹(需要PIN码才能启用指纹):

3. 如果使用的是模拟器,请确保它具有Play Store支持
在我们的实现和测试过程中,我们发现使用某些API版本/设备的Android模拟器时存在一些问题(但物理设备始终正常工作)。为了避免这些问题,请确保您使用的模拟器具有以下特性:
- API版本33或34
- Play Store支持(创建新模拟器设备时显示Google Play图标):

我们成功测试了此示例的设备包括:
- Pixel 7 Pro
- Pixel 7
- Pixel 6a
- Pixel 4
我们会持续更新此包以使其在更多模拟器上正常工作,一旦Google发布补丁。
如果您在Android模拟器上运行示例,建议您按照以下步骤操作:
- 启动您的Android模拟器(我们提到的模拟器之一)。
- 打开模拟器的设置并登录到您的Google账户。


- 保持在设置中添加屏幕锁(可选指纹)。

- 更新Google Play(扩展控制 => Google Play => 更新)。

- 重启模拟器(冷启动)。

- 您现在可以在Android模拟器上运行示例应用。
iOS
1. 确保启用生物特征以使用passkeys
如果在注册或登录时遇到错误,例如“模拟器需要注册生物特征才能使用passkeys”,请在您的设备上激活Face ID。在模拟器上,这可以在“功能” => “Face ID”下点击“注册”。

Web
1. 更新您的index.html以包含我们的JavaScript库
我们的passkeys_web包依赖于JavaScript来集成浏览器的WebAuthn API。为了让它工作,您必须在您的web/index.html文件中包含我们的JavaScript库。
<script src="https://github.com/corbado/flutter-passkeys/releases/download/2.0.0-dev.1/bundle.js" type="application/javascript"></script>
您也可以查看此包的示例以了解在那里是如何做到这一点的。
完整示例代码
import 'package:corbado_auth/corbado_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:passkeys_example/pages/loading_page.dart';
import 'package:passkeys_example/providers.dart';
import 'package:passkeys_example/router.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 如果需要初始化一些服务,这是一个不错的模式。
// 由于我们使用Riverpod,因此初始化发生在提供者中。
// 首先显示一个加载页面。
runApp(const LoadingPage());
// 现在我们进行初始化。
final relyingPartyServer = CustomCorbadoAuth();
const corbadoProjectId = String.fromEnvironment('CORBADO_PROJECT_ID');
await relyingPartyServer.init(corbadoProjectId);
runApp(
ProviderScope(
overrides: [
relyingPartyServerProvider.overrideWithValue(relyingPartyServer),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
theme: ThemeData(
useMaterial3: false,
colorScheme: const ColorScheme(
brightness: Brightness.light,
primary: Color(0xFF1953ff),
onPrimary: Colors.white,
secondary: Colors.white,
onSecondary: Colors.black,
error: Colors.redAccent,
onError: Colors.white,
background: Color(0xFF1953ff),
onBackground: Colors.white,
surface: Color(0xFF1953ff),
onSurface: Color(0xFF1953ff),
),
),
);
}
}
更多关于Flutter密码管理插件passkeys_capsule的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter密码管理插件passkeys_capsule的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
passkeys_capsule
是一个 Flutter 插件,用于在移动应用中管理和使用密码(Passkeys)。Passkeys 是一种基于 WebAuthn 的认证机制,允许用户使用生物识别(如指纹、面部识别)或设备 PIN 码进行身份验证,而无需输入传统的用户名和密码。
以下是使用 passkeys_capsule
插件的基本步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 passkeys_capsule
插件的依赖:
dependencies:
flutter:
sdk: flutter
passkeys_capsule: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 初始化插件
在你的 Dart 文件中导入插件并初始化:
import 'package:passkeys_capsule/passkeys_capsule.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PasskeysCapsule.initialize();
runApp(MyApp());
}
3. 注册 Passkey
要注册一个新的 Passkey,你可以使用 register
方法。通常,这需要从你的服务器获取一个 challenge
和其他必要的数据。
Future<void> registerPasskey() async {
try {
final registrationResponse = await PasskeysCapsule.register(
challenge: 'your_challenge_here',
rpId: 'your_rp_id_here',
userId: 'your_user_id_here',
userName: 'your_user_name_here',
displayName: 'your_display_name_here',
);
// 将 registrationResponse 发送到服务器进行验证
print('Registration successful: $registrationResponse');
} catch (e) {
print('Registration failed: $e');
}
}
4. 验证 Passkey
要验证一个 Passkey,你可以使用 authenticate
方法。同样,这需要从你的服务器获取一个 challenge
。
Future<void> authenticatePasskey() async {
try {
final authenticationResponse = await PasskeysCapsule.authenticate(
challenge: 'your_challenge_here',
rpId: 'your_rp_id_here',
);
// 将 authenticationResponse 发送到服务器进行验证
print('Authentication successful: $authenticationResponse');
} catch (e) {
print('Authentication failed: $e');
}
}
5. 处理服务器响应
在注册或验证 Passkey 后,你需要将响应发送到服务器进行进一步验证。服务器通常会验证签名的有效性,并确认用户的身份。
6. 错误处理
确保在处理 Passkey 注册和验证时捕获并处理可能出现的错误,例如用户取消操作、设备不支持等。
7. 示例代码
以下是一个简单的 Flutter 应用示例,展示了如何使用 passkeys_capsule
插件进行 Passkey 的注册和验证:
import 'package:flutter/material.dart';
import 'package:passkeys_capsule/passkeys_capsule.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PasskeysCapsule.initialize();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Passkeys Capsule Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: registerPasskey,
child: Text('Register Passkey'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: authenticatePasskey,
child: Text('Authenticate Passkey'),
),
],
),
),
),
);
}
Future<void> registerPasskey() async {
try {
final registrationResponse = await PasskeysCapsule.register(
challenge: 'your_challenge_here',
rpId: 'your_rp_id_here',
userId: 'your_user_id_here',
userName: 'your_user_name_here',
displayName: 'your_display_name_here',
);
print('Registration successful: $registrationResponse');
} catch (e) {
print('Registration failed: $e');
}
}
Future<void> authenticatePasskey() async {
try {
final authenticationResponse = await PasskeysCapsule.authenticate(
challenge: 'your_challenge_here',
rpId: 'your_rp_id_here',
);
print('Authentication successful: $authenticationResponse');
} catch (e) {
print('Authentication failed: $e');
}
}
}