Flutter密码管理插件passkeys_capsule的使用

Flutter密码管理插件passkeys_capsule的使用

GitHub Repo Cover

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。该流程如以下图像所示。

signup

首先,用户将在您的应用中提供其电子邮件地址或用户名。此电子邮件地址将发送到依赖方服务器。您可以自行实现该服务器,也可以依赖于预构建的服务器(例如由Corbado提供的)。 依赖方服务器的响应将包含publicKeyCredentialCreationOptions。它包含了用户设备设置passkey所需的所有信息。

您的应用现在会与本机设备的操作系统交互(无需担心,实际工作已由该Flutter包抽象化),以请求用户设置一个passkey。他只需要提供一次生物特征信息(例如通过Face ID或Touch ID)。在此设置之后,会创建一个私钥和公钥。私钥安全地存储在用户的设备上。公钥被发送到依赖方服务器。为了使这有效,应用必须与依赖方服务器关联。这涉及一些配置在您的应用和依赖方服务器上(有关详细信息,请参阅我们解释如何设置示例的部分)。 依赖方服务器将验证并存储公钥。之后,它将以成功消息(例如JWT令牌)响应。

从那时起,用户可以使用这些生物识别信息登录您的应用。每次登录时,您的应用都会向依赖方服务器请求一个挑战。依赖方服务器生成一个必须用私钥签名的挑战。为了访问该私钥,应用再次请求用户输入其生物特征(对本机设备操作系统发出另一次调用)。在用户提供了生物特征(例如通过指纹读取器)之后,挑战会被签名,并且签名后的挑战被发送到依赖方服务器。使用公钥,服务器将验证签名后的挑战,并以成功消息(例如JWT令牌)响应。

示例

要运行示例,请遵循以下步骤:

  1. 克隆GitHub仓库 git clone git@github.com:corbado/flutter-passkeys.git
  2. 如果您想在iOS模拟器或Android模拟器上运行示例,请现在启动一个(如果您想在浏览器中启动示例,则跳过此步骤)。
  3. 运行 melos run example-passkeys-native 以在iOS/Android上启动示例。
  4. 提供电子邮件地址并点击“注册”来注册新用户。
  5. 登出后,您可以使用相同的电子邮件地址再次登录,或者提供一个新的电子邮件地址来注册另一个用户。

有关更多说明和故障排除,请参阅示例的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账户。

troubleshooting_google_account

2. 确保已在设备上设置屏幕锁或生物特征

如果在模拟器中运行应用程序时提示无法创建passkeys,请登录到您的Google账户并正确设置屏幕锁或生物特征。

要设置屏幕锁,请打开设置,搜索安全设置并添加一个PIN码以及指纹(需要PIN码才能启用指纹):

troubleshooting_screen_lock

3. 如果使用的是模拟器,请确保它具有Play Store支持

在我们的实现和测试过程中,我们发现使用某些API版本/设备的Android模拟器时存在一些问题(但物理设备始终正常工作)。为了避免这些问题,请确保您使用的模拟器具有以下特性:

  • API版本33或34
  • Play Store支持(创建新模拟器设备时显示Google Play图标):
android-play-store-support

我们成功测试了此示例的设备包括:

  • Pixel 7 Pro
  • Pixel 7
  • Pixel 6a
  • Pixel 4

我们会持续更新此包以使其在更多模拟器上正常工作,一旦Google发布补丁。

如果您在Android模拟器上运行示例,建议您按照以下步骤操作:

  1. 启动您的Android模拟器(我们提到的模拟器之一)。
  2. 打开模拟器的设置并登录到您的Google账户。
sign-in-to-google-account sign-in-to-google-account-details
  1. 保持在设置中添加屏幕锁(可选指纹)。
add-screen-lock
  1. 更新Google Play(扩展控制 => Google Play => 更新)。
update-google-play
  1. 重启模拟器(冷启动)。
restart-emulator
  1. 您现在可以在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

1 回复

更多关于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');
    }
  }
}
回到顶部