Flutter身份验证插件sbt_auth_dart的使用

Flutter身份验证插件sbt_auth_dart的使用

SBTAuth SDK for flutter.

Setup

iOS

在 Info.plist 文件中添加自定义 URL 方案。

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Viewer</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>{your custom scheme}</string>
    </array>
  </dict>
</array>

Android

在 AndroidManifest.xml 文件中添加 intent-filter 内部的 activity。

<activity ...>
  <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="{your custom scheme}" />
  </intent-filter>
</activity>

初始化 SBTAuth

注意:如果 developModetrue,则连接至测试服务,测试服务邮箱登录无需验证码,同时测试服务仅可连接至测试网。请在正式发布时确保 developModefalse。 SBTAuth Wallet 目前支持网络包括 Ethereum, Polygon, BNB Smart Chain。

// 初始化 SBTAuth 前需要先调用 initHive 方法,一般放在 main.dart 中
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SbtAuth.initHive();
  runApp(const MyApp());
}

// 初始化 sbtAuth
SbtAuth auth = SbtAuth(developMode: true, clientId: 'Demo', scheme: 'Custom scheme');

登录 SBTAuth 账户

SBTAuth 目前支持邮箱登录、Google Account、Facebook、Twitter。 如果使用邮箱验证码登录,需要先获取验证码。

// 传入邮箱 email 发送验证码
await auth.api.sendVerifyCode(email);

// 使用邮箱登录,传入邮箱 email,然后传入验证码 code 或者密码 password 进行登录
await auth.login(LoginType.email, {email: 'example@gmail.com', code: '121212'});

// 使用第三方登录,只需要传入 LoginType
await auth.login(LoginType.google);

登录成功后会获取用户信息,如果是新用户会直接创建账户进入 APP,可以设置登录密码, 并且设置安全码,得到加密后的私钥碎片, 支持发送加密碎片到邮箱。

if (auth.user?.backupPrivateKey != null) {
  // 说明是新注册用户,推荐此时提醒用户通过邮箱备份。备份私钥时需用户输入安全密码,安全密码用于对私钥进行加密,保证备份私钥安全。
  // 传入密码 password 来设置账户的登录密码, 同时可以设置支付密码 paymentPassword, paymentSwitch 表示是否开启密码
  await auth.api.setPassword(password, paymentPassword: "123456", paymentSwitch: true);

  // 如果用户开启了支付密码(auth.user.paymentSwitch == true), 那么交易之前需要先验证支付密码 paymentPassword, 验证密码正确后再发起交易
  await auth.api.checkPaymentPassword(paymentPassword);

  // 传入加密的私钥碎片 encryptPrivateKey, 邮箱 email,和邮箱验证码 code 来发送备份的私钥碎片
  // chan 为备份的碎片的链, 不传默认为 EVM
  await auth.sendBackupPrivateKey(encryptPrivateKey, email, code, chain: chain);
}

如果是已注册用户,并且在新设备登录,则需要恢复私钥碎片,可以通过已登录设备授权的方式恢复,也可以通过原来备份的私钥碎片进行恢复。

已登录设备授权恢复

// 需要先获取已登录设备列表
final deviceList = await auth.api.getUserDeviceList();

// 从列表中选择设备,传入设备名 deviceName, 需要恢复的链 chain 调用方法来请求已登录设备授权
await auth.api.sendAuthRequest(deviceName, chain: chain);

// 输入已登录设备生成的授权码 code, 要恢复的链 chain 获取授权完成恢复
await auth.recoverWithDevice(code, chain: chain);

通过保存的私钥碎片恢复

// chain 表示需要恢复的链, 不传默认恢复 EVM
await auth.recoverWidthBackup(backupPrivateKey, password, chain: chain);

已登录设备在账户登录之后,或者在 APP 初始化且账户登录未过期时添加监听。

sbtAuth.authRequestStreamController.stream.listen((event) {
  // 获取新设备名 deviceName
  final deviceName = jsonDecode(event)['deviceName'];
  // 获取请求授权恢复的链 keyType
  final keyType = jsonDecode(event)['keyType'];
  // 获取新设备名称后传入得到的设备名 deviceName, 链名 keyType 需要生成授权码授权新设备登录
  final authCode = await sbtAuth.approveAuthRequest(deviceName, chain: SbtChain.values.byName(keyType));
});

使用 SBTAuth 钱包

调用 personal_sign 方法对消息进行签名

final provider = auth.provider;

// 传入要签名的数据 message 来获取签名
final signature = await provider.request(RequestArgument(method: 'personal_sign', params: [message]));

发送交易

EVM
/// EVM
final provider = auth.provider;

// 发送交易之前首先要设置 chainId 如:'0x5'
provider.setChainId(chainId);

// 传入交易的信息之后调用 sendTransaction() 方法即可获取交易 hash
final txHash = await provider.sendTransaction(
  to: to,
  value: value,
  data: data,
  gasPrice: gasPrice
);
SOLANA
/// SOLANA
// Solana 的原生币和代币转账是不一样的, 代币转账需要先查询自己和收款方的代币关联账户, 并用自己的关联账户向对方关联账户转账代币,
// 如果收款方没有关联账户, 则需要先调用 createAssociatedTokenAccount() 方法给收款方创建关联账户
// 所有的交易只需要构造 Instruction 调用 sendTransaction() 方法来发送交易
final signer = sbtAuth.solanaSigner!;

// 转账需要传入构造的交易 instruction 和发送方 from
final hash = await signer.sendTransaction(instruction, from);

// 创建关联账户只需要传入交易发送方 from 交易接收方 to 以及代币地址 tokenAddress 即可
final hash = await signer.createAssociatedTokenAccount(from, to, tokenAddress);
// * 注意 创建关联账户 hash 需要上链成功之后才可以进行代币转账
BITCOIN
/// BITCOIN
final btcSigner = sbtAuth.bitcoinSigner!;

// Bitcoin 转账只需要传入交易发送方 from, 交易接收方 to 以及交易数量 amount 即可
// amount 不得小于 1000
final hash = await btcSigner.sendBtcTransaction(from, to, amount);

白名单相关功能

交易白名单是更高级的安全模式,开启白名单之后账户只能对白名单内的地址进行转账交易。

// 开启修改关闭等对白名单的操作都需要进行邮箱验证码验证,所以如果用户没有绑定邮箱,则需要提示用户先备份私钥并绑定邮箱

// 输入加密私钥的安全码 password, 邮箱 email, 邮箱验证码 code
await sbtAuth.sendBackupPrivateKey(password, email, code);

// 开启/关闭白名单
// 输入邮箱验证码 authCode, 白名单开启关闭状态 whitelistSwitch 来开启或关闭白名单
await sbtAuth.switchWhiteList(authCode, whitelistSwitch: whitelistSwitch);

// 获取白名单列表
// 可选参数 network, 输入即可查询对应网络的白名单列表, 不输即为所有网络的白名单列表
final whiteList = await sbtAuth.api.getUserWhiteList(page, pageSize, network: network);

// 获取单个白名单详情
// 输入白名单的 id userWhitelistID 来获取详情
final whiteListItem = await sbtAuth.api.getUserWhiteListItem(userWhitelistID);

// 新增白名单
// 输入邮箱验证码 authCode, 白名单地址 address, 白名单名称 name, 白名单网络 network 新增白名单地址, tolowerCase 表示是否需要小写, EVM 系需小写
await sbtAuth.createWhiteList(authCode, address, name, network, tolowerCase: false);

// 删除白名单
// 输入邮箱验证码 authCode, 白名单 id userWhitelistID 来删除白名单
await sbtAuth.deleteWhiteList(authCode, userWhitelistID);

// 修改白名单
// 输入邮箱验证码 authCode, 白名单地址 address, 白名单名称 name, 白名单 id userWhitelistID, 用户 id userId 和白名单网络 network 来修改白名单, tolowerCase 表示是否需要小写, EVM 系需小写
await sbtAuth.editWhiteList(authCode, address, name, userWhitelistID, userId, network, tolowerCase: true);

其他功能

可以设置登录有效期,延长或缩短登录过期时间。

// 只需要调用 setUserToken() 方法, 传入登录有效时间 tokenTime (单位:分钟)
await auth.api.setUserToken(tokenTime);

目前支持的网络以及网络名称 network

// 以太坊
ETH("eth", 1),

// 币安链
BSC("bsc", 56),

// Polygon
Polygon("polygon", 137),

// Solana
Solana("solana")

/// Bitcoin
Bitcoin("bitcoin")

// 以太坊测试网 Goerli
Goerli("eth_goerli", 5),

// 币安链测试网
BSC_test("bsc_chapel", 97),

// Polygon testnet
Polygon_test("polygon_mumbai", 80001),

// Solana devnet
Solana_devnet("solana_devnet")

// Bitcoin testnet
Bitcoin_testnet("btc_testnet")

示例代码

以下是完整的示例代码,展示了如何使用 sbt_auth_dart 插件进行身份验证和操作。

import 'dart:convert';
import 'dart:developer';

import 'package:example/confirm_auth.dart';
import 'package:example/grant_authorization.dart';
import 'package:example/sign.dart';
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:sbt_auth_dart/sbt_auth_dart.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SbtAuth.initHive();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late TextEditingController _controller;
  final sbtAuth = SbtAuth(developMode: true, clientId: 'Demo', scheme: 'sbtauth');
  String code = '';

  [@override](/user/override)
  void initState() {
    super.initState();
    getCode();
    _controller = TextEditingController();
    _controller.text = '30min18@gmail.com';
    sbtAuth.authRequestStreamController.stream.listen((event) {
      if (event.contains('deviceName')) {
        final deviceName = jsonDecode(event)['deviceName'];
        final keyType = jsonDecode(event)['keyType'];
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => ConfirmAuthPage(
                      deviceName: deviceName,
                      auth: sbtAuth,
                      keyType: keyType,
                    )));
      }
    });
    sbtAuth.loginStreamController.stream.listen((event) {
      if (event) {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => SignPage(
              address: sbtAuth.user!.publicKeyAddress['EVM']['address'] ??
                  sbtAuth.core!.getAddress(isTestnet: sbtAuth.developMode),
              sbtauth: sbtAuth,
            ),
          ),
        );
      }
    });
  }

  getCode() async {
    final res = await sbtAuth.getLoginQrCode();
    setState(() {
      code = res;
    });
  }

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              GestureDetector(
                onTap: () async {
                  final res = await sbtAuth.getLoginQrCode();
                  setState(() {
                    code = res;
                  });
                },
                child: QrImageView(data: code),
              ),
              TextField(
                controller: _controller,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Email',
                ),
              ),
              ElevatedButton(
                  onPressed: () {
                    _login(LoginType.email, email: _controller.text.trim());
                  },
                  child: const Text('Login with email')),
              ElevatedButton(
                  onPressed: () {
                    _login(LoginType.google);
                  },
                  child: const Text('Login with Google')),
              ElevatedButton(
                  onPressed: () {
                    _login(LoginType.facebook);
                  },
                  child: const Text('Login With Facebook')),
              ElevatedButton(
                  onPressed: () {
                    _login(LoginType.twitter);
                  },
                  child: const Text('Login With Twitter')),
            ],
          ),
        ),
      ),
    );
  }

  _login(LoginType loginType, {String? email, String? password}) async {
    try {
      await sbtAuth.login(loginType,
          email: email, code: '203687', password: password);
      if (mounted) {
        if (sbtAuth.provider == null) {
          Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => GrantAuthorizationPage(
                        auth: sbtAuth,
                      )));
        } else {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => SignPage(
                address: sbtAuth.user!.publicKeyAddress['EVM'] == null
                    ? sbtAuth.core!.getAddress(isTestnet: sbtAuth.developMode)
                    : sbtAuth.user!.publicKeyAddress['EVM']['address'],
                sbtauth: sbtAuth,
              ),
            ),
          );
        }
      }
    } catch (e) {
      if (e is SbtAuthException) {
        log(e.toString());
        if (e.toString() == 'New device detected') {}
      }
    }
  }
}

更多关于Flutter身份验证插件sbt_auth_dart的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter身份验证插件sbt_auth_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


SbtAuthDart 是一个用于 Flutter 应用的身份验证插件,它提供了简单易用的 API 来处理用户登录、注册、注销等身份验证相关的功能。以下是如何在 Flutter 项目中使用 SbtAuthDart 插件的步骤。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 sbt_auth_dart 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  sbt_auth_dart: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 初始化插件

在使用 SbtAuthDart 插件之前,你需要在应用的入口处初始化它。通常是在 main.dart 文件中进行初始化。

import 'package:flutter/material.dart';
import 'package:sbt_auth_dart/sbt_auth_dart.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化 SbtAuthDart
  await SbtAuthDart.initialize(
    apiKey: 'YOUR_API_KEY',  // 替换为你的 API Key
    authDomain: 'YOUR_AUTH_DOMAIN',  // 替换为你的认证域
  );

  runApp(MyApp());
}

3. 用户注册

你可以使用 SbtAuthDart 提供的 register 方法来注册新用户。

void registerUser() async {
  try {
    final user = await SbtAuthDart.register(
      email: 'user@example.com',
      password: 'password123',
    );
    print('User registered: ${user.email}');
  } catch (e) {
    print('Registration failed: $e');
  }
}

4. 用户登录

使用 login 方法来登录用户。

void loginUser() async {
  try {
    final user = await SbtAuthDart.login(
      email: 'user@example.com',
      password: 'password123',
    );
    print('User logged in: ${user.email}');
  } catch (e) {
    print('Login failed: $e');
  }
}

5. 用户注销

使用 logout 方法来注销当前用户。

void logoutUser() async {
  try {
    await SbtAuthDart.logout();
    print('User logged out');
  } catch (e) {
    print('Logout failed: $e');
  }
}

6. 获取当前用户

你可以使用 currentUser 方法来获取当前登录的用户。

void getCurrentUser() {
  final user = SbtAuthDart.currentUser;
  if (user != null) {
    print('Current user: ${user.email}');
  } else {
    print('No user is currently logged in');
  }
}

7. 监听身份验证状态

SbtAuthDart 还提供了身份验证状态变化的监听功能。

void listenToAuthState() {
  SbtAuthDart.authStateChanges.listen((user) {
    if (user != null) {
      print('User is logged in: ${user.email}');
    } else {
      print('User is logged out');
    }
  });
}

8. 处理错误

在进行身份验证操作时,可能会遇到各种错误。SbtAuthDart 会抛出异常,你可以通过 try-catch 块来处理这些错误。

void handleErrors() async {
  try {
    await SbtAuthDart.login(
      email: 'wrong@example.com',
      password: 'wrongpassword',
    );
  } catch (e) {
    print('Error: $e');
  }
}
回到顶部