Flutter身份验证保护插件auth0_guardian的使用
Flutter身份验证保护插件auth0_guardian的使用
介绍
auth0_guardian
是一个社区开发的插件,封装了Auth0的Guardian SDK,支持在Flutter应用中方便地集成多因素身份验证(MFA)服务。Guardian是Auth0提供的多因素认证服务,能够为用户提供简单且安全的MFA实现。
Auth0是一个身份验证代理,支持社交身份提供商(如Google、Facebook等)以及企业身份提供商(如Active Directory、LDAP、Google Apps和Salesforce等)。通过这个SDK,你可以在自己的应用中集成Auth0的Guardian多因素服务,将应用本身作为第二因素,让用户从你的应用中享受到无摩擦的多因素认证体验。
入门指南
要求
平台 | 版本要求 |
---|---|
Flutter | SDK 3.3.0+,Dart 3.4.3+ |
Android | API 23+,Java 8+ |
iOS | iOS 13+,Swift 5.0+ |
安装
要使用auth0_guardian
插件,首先需要将其添加到你的项目中:
flutter pub add auth0_guardian
准备工作
在使用此SDK之前,你需要为你的Auth0租户配置Guardian服务,并提供你自己的推送通知凭证(如Firebase或APNs)。具体配置方法可以参考官方文档。
此外,你还需要在应用中配置一个通知系统(如Firebase或其他推送服务)。
功能
功能 | Android | iOS |
---|---|---|
注册设备 | ✅ | ✅ |
删除设备(注销) | ✅ | ✅ |
接受请求 | ✅ | ✅ |
拒绝请求 | ✅ | ✅ |
更新设备 | ❌ | ✅ |
使用示例
以下是一个完整的示例代码,展示了如何在Flutter应用中使用auth0_guardian
插件进行设备注册、接收和处理登录请求等功能。
import 'dart:developer';
import 'package:auth0_guardian/auth0_guardian.dart';
import 'package:auth0_guardian_example/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:push/push.dart' as push;
/// 测试用的租户URL
const kTenantUrl = "https://<YOUR_TENANT>/appliance-mfa";
void main() {
runApp(const GuardianExampleApp());
}
class GuardianExampleApp extends StatefulWidget {
const GuardianExampleApp({super.key});
[@override](/user/override)
State<GuardianExampleApp> createState() => _GuardianExampleAppState();
}
class _GuardianExampleAppState extends State<GuardianExampleApp> {
/// Auth0 Guardian实例
final guardian = Guardian(tenantUrl: kTenantUrl);
/// 设备的通知令牌
String? notificationToken;
/// 注册URI
String? enrollUri;
/// 已注册的设备
EnrolledDevice? enrolledDevice;
/// 存储最新的通知消息
push.RemoteMessage? latestNotification;
/// 获取设备的通知令牌
void setNotificationToken() async {
// 初始化Firebase
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// 请求推送通知权限
await FirebaseMessaging.instance.requestPermission();
// 获取APNs令牌(iOS)
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
setState(() => notificationToken = apnsToken);
} else {
// 获取Firebase令牌(Android)
final token = await FirebaseMessaging.instance.getToken();
setState(() => notificationToken = token);
}
}
/// 扫描二维码获取注册URI
void scanBarcode() {
FlutterBarcodeScanner.scanBarcode(
"#ff6666",
'取消',
false,
ScanMode.QR,
).then((qr) {
setState(() => enrollUri = Uri.decodeFull(qr));
});
}
/// 注册设备
void enroll(BuildContext context) async {
final messenger = ScaffoldMessenger.of(context);
try {
// 使用注册URI和通知令牌进行设备注册
final EnrolledDevice device = await guardian.enroll(
usingUri: enrollUri!,
notificationToken: notificationToken!,
);
setState(() => enrolledDevice = device);
log(device.toString());
} catch (e, st) {
if (kDebugMode) {
print(e);
print(st);
}
messenger.showSnackBar(SnackBar(content: Text('错误: $e')));
}
}
/// 删除已注册的设备
void deleteDevice(BuildContext context, EnrolledDevice device) async {
final messenger = ScaffoldMessenger.of(context);
try {
// 删除设备
final result = await guardian.deleteDevice(device);
if (result) setState(() => enrolledDevice = null);
} catch (e, st) {
if (kDebugMode) {
print(e);
print(st);
}
messenger.showSnackBar(SnackBar(content: Text('错误: $e')));
}
}
/// 更新已注册的设备(仅限iOS)
void updateDevice(BuildContext context, EnrolledDevice device) async {
final messenger = ScaffoldMessenger.of(context);
try {
// 更新设备名称
await guardian.updateDevice(
device,
name: '更新后的名称',
notificationToken: notificationToken,
localIdentifier: null,
);
} catch (e, st) {
if (kDebugMode) {
print(e);
print(st);
}
messenger.showSnackBar(SnackBar(content: Text('错误: $e')));
}
}
/// 拒绝最新的登录请求
Future<void> rejectRequest() async {
final payload = latestNotification?.data;
if (payload != null) {
// 拒绝登录请求
final result = await guardian.rejectRequest(notification: payload);
if (result) {
setState(() => latestNotification = null);
}
}
}
/// 接受最新的登录请求
Future<void> acceptRequest() async {
final payload = latestNotification?.data;
if (payload != null) {
// 接受登录请求
await guardian.acceptRequest(notification: payload);
}
}
[@override](/user/override)
void initState() {
super.initState();
// 添加通知监听器
push.Push.instance.addOnMessage(onNotification);
push.Push.instance.addOnBackgroundMessage(onNotification);
// 获取通知令牌
setNotificationToken();
}
/// 处理接收到的通知
void onNotification(push.RemoteMessage message) async {
// 检查通知是否包含数据
if (message.data == null) return;
// 检查通知是否来自Guardian
final isValid = await guardian.isGuardianNotification(message.data!);
if (isValid) {
log("接收到Guardian通知: ${message.data}");
setState(() => latestNotification = message);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('🔑 Auth0 Guardian 示例应用'),
),
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: Builder(builder: (context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
Visibility(
visible: notificationToken == null,
replacement: _TextLine(
label: '通知令牌 (APNs / Firebase)',
value: notificationToken ?? '',
),
child: ElevatedButton(
onPressed: setNotificationToken,
child: const Text('设置 (APNs / Firebase) 令牌'),
),
),
const Divider(),
const SizedBox(height: 4),
ElevatedButton(
onPressed: scanBarcode,
child: const Text('步骤 1: 扫描二维码'),
),
const SizedBox(height: 8),
Visibility(
visible: enrollUri != null,
child: _TextLine(
label: '注册 URI',
value: enrollUri ?? '',
),
),
const Divider(),
const SizedBox(height: 4),
ElevatedButton(
onPressed: () => enroll(context),
child: const Text('步骤 2: 触发注册! 🚀'),
),
const SizedBox(height: 8),
Visibility(
visible: enrolledDevice != null,
child: _TextLine(
label: '已注册的设备',
value: enrolledDevice?.id ?? '',
),
),
enrolledDevice != null
? Column(
children: [
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => deleteDevice(
context,
enrolledDevice!,
),
child: Text(
'删除设备 ${enrolledDevice?.id}',
),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => updateDevice(
context,
enrolledDevice!,
),
child: const Text(
'更新设备 (仅限iOS)',
),
),
],
)
: Container(),
const Divider(),
const SizedBox(height: 4),
const Text(
'步骤 3: 等待通知',
style: TextStyle(
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
if (latestNotification != null)
Column(
children: [
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: acceptRequest,
child: const Text('接受'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: rejectRequest,
child: const Text('拒绝'),
),
],
),
const SizedBox(height: 8),
Text(latestNotification?.data.toString() ?? ''),
],
)
],
),
);
}),
),
),
);
}
}
class _TextLine extends StatelessWidget {
const _TextLine({
required this.label,
required this.value,
});
/// 文本标签
final String label;
/// 文本值
final String value;
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label: ',
style: const TextStyle(
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
const SizedBox(height: 4),
Text(value)
],
);
}
}
更多关于Flutter身份验证保护插件auth0_guardian的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter身份验证保护插件auth0_guardian的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter项目中使用auth0_guardian
插件来实现身份验证保护的示例代码。auth0_guardian
通常用于增强身份验证流程,通过添加第二层保护(例如,通过短信或电子邮件发送一次性密码)。
首先,确保你已经在pubspec.yaml
文件中添加了auth0_guardian
依赖:
dependencies:
flutter:
sdk: flutter
auth0_guardian: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
配置Auth0
在使用auth0_guardian
之前,你需要在Auth0平台上进行一些配置,包括创建一个应用并启用Guardian。这些步骤在Auth0的官方文档中会有详细的说明。
Flutter代码示例
以下是一个基本的Flutter应用示例,展示了如何使用auth0_guardian
进行身份验证保护:
import 'package:flutter/material.dart';
import 'package:auth0_guardian/auth0_guardian.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Auth0 Guardian Example'),
),
body: Center(
child: GuardianExample(),
),
),
);
}
}
class GuardianExample extends StatefulWidget {
@override
_GuardianExampleState createState() => _GuardianExampleState();
}
class _GuardianExampleState extends State<GuardianExample> {
final Auth0Guardian _auth0Guardian = Auth0Guardian();
String? _enrollmentId;
String? _oneTimePassword;
void _startEnrollment() async {
try {
// 替换为你的Auth0域名和客户端ID
String auth0Domain = 'your-auth0-domain.com';
String clientId = 'your-client-id';
String userId = 'user-id-or-email'; // 要保护的用户ID或电子邮件
// 开始注册流程
String? enrollmentId = await _auth0Guardian.startEnrollment(
auth0Domain: auth0Domain,
clientId: clientId,
userId: userId,
channel: 'sms', // 或者 'email'
);
if (enrollmentId != null) {
setState(() {
_enrollmentId = enrollmentId;
});
}
} catch (e) {
print('Enrollment error: $e');
}
}
void _verifyEnrollment() async {
try {
// 替换为你的Auth0域名和客户端ID
String auth0Domain = 'your-auth0-domain.com';
String clientId = 'your-client-id';
String? otp = _oneTimePassword; // 用户输入的一次性密码
// 验证注册
bool isVerified = await _auth0Guardian.verifyEnrollment(
auth0Domain: auth0Domain,
clientId: clientId,
enrollmentId: _enrollmentId!,
otp: otp!,
);
if (isVerified) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Success'),
content: Text('Enrollment verified successfully!'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text('Failed to verify enrollment.'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
}
} catch (e) {
print('Verification error: $e');
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: _startEnrollment,
child: Text('Start Enrollment'),
),
SizedBox(height: 20),
TextField(
decoration: InputDecoration(labelText: 'Enter OTP'),
onChanged: (value) {
setState(() {
_oneTimePassword = value;
});
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _verifyEnrollment,
child: Text('Verify Enrollment'),
),
],
);
}
}
注意事项
-
替换占位符:确保将
your-auth0-domain.com
、your-client-id
和user-id-or-email
替换为你的实际Auth0域名、客户端ID和用户ID或电子邮件。 -
错误处理:在实际应用中,应添加更详细的错误处理和用户反馈。
-
安全性:不要在客户端代码中硬编码敏感信息,如Auth0的客户端密钥或用户密码。
-
UI/UX:根据实际需求调整UI/UX设计,确保用户体验流畅。
这个示例展示了如何使用auth0_guardian
插件启动注册流程并验证一次性密码。如果你需要更高级的功能,请参考Auth0的官方文档和auth0_guardian
插件的README文件。