• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Face_auth
2
3## 概述
4
5### 功能简介
6
7人脸识别功能是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。它是基于人的脸部特征信息进行身份识别的一种生物特征识别技术,用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别,通常也叫做人像识别、面部识别、人脸认证。人脸识别功能整体框架如图1。
8
9基于HDF(Hardware Driver Foundation)驱动框架开发的Face_auth驱动,能够屏蔽硬件器件差异,为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力接口,包括人脸识别执行器列表查询、执行器信息查询、指定人脸模板ID查询模板信息、用户认证框架和执行器间的人脸模板信息对账、人脸录入、删除、认证和识别等。
10
11**图1** 人脸识别功能整体框架
12
13![image](figures/人脸识别功能整体框架图.png "人脸识别功能整体框架图")
14
15### 基本概念
16
17用户认证框架与各基础认证服务组成的身份认证系统支持用户认证凭据设置、删除、认证等基础功能。系统支持用户身份认证,需要提供数据采集、处理、存储及比对能力。
18- 执行器
19
20  执行器是能够提供以上能力的处理模块,各基础认证服务提供执行器能力,被身份认证框架调度完成各项基础能力。
21
22- 执行器安全等级
23
24  执行器提供能力时所在运行环境达到的安全级别。
25
26- 执行器角色
27
28  - 全功能执行器:执行器可独立处理一次凭据注册和身份认证请求,即可提供用户认证数据采集、处理、储存及比对能力。
29
30  - 采集器:执行器提供用户认证时的数据采集能力,需要和认证器配合完成用户认证。
31
32  - 认证器:认证器提供用户认证时数据处理能力,读取存储的凭据模板与当前认证信息完成比对。
33
34- 执行器类型
35
36  同一种身份认证类型的不同认证方式会产生认证算法差异,设备器件差异也会导致算法差异,执行器根据支持的算法类型差异或对接的器件差异,会定义不同的执行器类型。
37
38- 用户认证框架公钥 & 执行器公钥
39
40  用户身份认证处理需要保证用户数据安全以及认证结果的准确性,用户认证框架与基础认证服务间的关键交互信息需要做数据完整性保护,各基础认证服务将提供的执行器能力对接到用户认证框架时,需要交换各自的公钥,其中:
41
42    1)执行器通过用户认证框架公钥校验调度指令的准确性。
43
44    2)执行器公钥可被用户认证框架用于校验认证结果的准确性,同时用于执行器交互认证时的校验交互信息的完整性。
45
46- 认证凭据模板
47
48  认证凭据是在用户设置认证凭据时由认证服务产生并存储,每个模板有一个ID,用于索引模板信息文件,在认证时读取模板信息并用于与当次认证过程中产生的认证数据做对比,完成身份认证。
49
50- 执行器对账
51
52  用户认证框架统一管理用户身份和凭据ID的映射关系,执行器对接到用户认证框架时,会读取用户身份认证框架内保存的该执行器的模板ID列表,执行器需要与自己维护的模板ID列表进行比对,并删除冗余信息。
53
54- HAPs
55
56  HAPs(OpenHarmony Ability Packages),广义上指可以安装在OpenHarmony上的应用包,本章节中仅代表Face_auth驱动的上层应用。
57
58- IDL接口
59
60  接口定义语言(Interface Definition Language)通过IDL编译器编译后,能够生成与编程语言相关的文件:客户端桩文件,服务器框架文件。本文主要是通过IDL接口生成的客户端和服务端来实现Face_auth服务和驱动的通信,详细使用方法可参考[IDL简介](https://gitee.com/openharmony/ability_idl_tool/blob/master/README.md)61
62- IPC通信
63
64  IPC(Inter Process Communication),进程间通信是指两个进程的数据之间产生交互,详细原理可参考[IPC通信简介](https://gitee.com/openharmony/communication_ipc/blob/master/README_zh.md)65
66- HDI
67
68  HDI(Hardware Device Interface),硬件设备接口,位于基础系统服务层和设备驱动层之间,是提供给硬件系统服务开发者使用的、统一的硬件设备功能抽象接口,其目的是为系统服务屏蔽底层硬件设备差异,具体可参考[HDI规范](../../design/hdi-design-specifications.md)。
69
70### 运作机制
71
72Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。
73开发者可基于HDF框架对不同芯片进行各自驱动的开发及HDI层接口的调用。
74
75**图2** Face_auth服务和Face_auth驱动交互
76
77![image](figures/人脸识别服务和faceauth驱动接口.png "人脸识别服务和faceauth驱动接口")
78
79### 约束与限制
80
81- 要求设备上具备摄像器件,且人脸图像像素大于100*100。
82- 要求设备上具有可信执行环境,人脸特征信息高强度加密保存在可信执行环境中。
83- 对于面部特征相似的人、面部特征不断发育的儿童,人脸特征匹配率有所不同。如果对此担忧,可考虑其他认证方式。
84
85## 开发指导
86
87### 场景介绍
88
89Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。
90
91### 接口说明
92
93注:以下接口列举的为IDL接口描述生成的对应C++语言函数接口,接口声明见idl文件(/drivers/interface/face_auth/v1_0/)。
94
95在本文中,人脸凭据的录入、认证、识别和删除相关的HDI接口如表1所示,表2中的回调函数分别用于人脸执行器返回操作结果给框架和返回操作过程中的提示信息给上层应用。
96
97**表1** 接口功能介绍
98
99| 接口名称       | 功能介绍         |
100| ----------------------------------- | ---------------------------------- |
101| GetExecutorList(std::vector<sptr<IExecutor>>& executorList)  | 获取执行器列表。            |
102| GetExecutorInfo(ExecutorInfo& info)      | 获取执行器信息,包括执行器类型、执行器角色、认证类型、安全等级、执行器公钥等信息,用于向用户认证框架注册执行器。 |
103| GetTemplateInfo(uint64_t templateId, TemplateInfo& info)     | 获取指定人脸模板ID的模板信息。        |
104| OnRegisterFinish(const std::vector<uint64_t>& templateIdList,<br/>        const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo) | 执行器注册成功后,获取用户认证框架的公钥信息;获取用户认证框架的人脸模板列表用于对账。 |
105| Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,<br/>        const sptr<IExecutorCallback>& callbackObj) | 录入人脸模板。             |
106| Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList,<br/>        const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 认证人脸模板。         |
107| Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,<br/>        const sptr<IExecutorCallback>& callbackObj) | 识别人脸模板。                                               |
108| Delete(const std::vector<uint64_t>& templateIdList)          | 删除人脸模板。  |
109| Cancel(uint64_t scheduleId)                                  | 通过scheduleId取消指定录入、认证、识别操作。  |
110| SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo,<br/>        const sptr<IExecutorCallback>& callbackObj) | 人脸认证服务向Face_auth驱动传递参数的通用接口。              |
111
112**表2** 回调函数介绍
113
114| 接口名称                                                       | 功能介绍                 |
115| ------------------------------------------------------------ | ------------------------ |
116| IExecutorCallback::OnResult(int32_t code, const std::vector<uint8_t>& extraInfo) | 返回操作的最终结果。     |
117| IExecutorCallback::OnTip(int32_t code, const std::vector<uint8_t>& extraInfo) | 返回操作的过程交互信息。 |
118
119### 开发步骤
120
121以Hi3516DV300平台为例,我们提供了Face_auth驱动DEMO实例,以下是目录结构及各部分功能简介。
122
123```undefined
124// drivers/peripheral/face_auth
125├── BUILD.gn     # 编译脚本
126├── bundle.json  # 组件描述文件
127└── hdi_service  # Face_auth驱动实现
128    ├── BUILD.gn    # 编译脚本
129    ├── include     # 头文件
130    └── src         # 源文件
131        ├── executor_impl.cpp                # 认证、录入等功能接口实现
132        ├── face_auth_interface_driver.cpp   # Face_auth驱动入口
133        └── face_auth_interface_service.cpp  # 获取执行器列表接口实现
134```
135
136下面结合DEMO实例介绍驱动开发的具体步骤。
137
1381. 基于HDF驱动框架,按照驱动Driver Entry程序,完成Face_auth驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现,详细代码参见[face_auth_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_driver.cpp)文件。
139
140   ```c++
141   // 通过自定义的HdfFaceAuthInterfaceHost对象包含ioService对象和真正的HDI Service实现IRemoteObject对象
142   struct HdfFaceAuthInterfaceHost {
143       struct IDeviceIoService ioService;
144       OHOS::sptr<OHOS::IRemoteObject> stub;
145   };
146
147   // 服务接口调用响应接口
148   static int32_t FaceAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,
149       struct HdfSBuf *reply)
150   {
151       IAM_LOGI("start");
152       auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(client->device->service,
153           struct HdfFaceAuthInterfaceHost, ioService);
154
155       OHOS::MessageParcel *dataParcel = nullptr;
156       OHOS::MessageParcel *replyParcel = nullptr;
157       OHOS::MessageOption option;
158
159       if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {
160           IAM_LOGE("%{public}s:invalid data sbuf object to dispatch", __func__);
161           return HDF_ERR_INVALID_PARAM;
162       }
163       if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {
164           IAM_LOGE("%{public}s:invalid reply sbuf object to dispatch", __func__);
165           return HDF_ERR_INVALID_PARAM;
166       }
167
168       return hdfFaceAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
169   }
170
171   // 初始化接口
172   int HdfFaceAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject)
173   {
174       IAM_LOGI("start");
175       if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_USERAUTH)) {
176           IAM_LOGE("set face auth hdf class failed");
177           return HDF_FAILURE;
178       }
179       return HDF_SUCCESS;
180   }
181
182   // Face_auth驱动对外提供的服务绑定到HDF框架
183   int HdfFaceAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject)
184   {
185       IAM_LOGI("start");
186       auto *hdfFaceAuthInterfaceHost = new (std::nothrow) HdfFaceAuthInterfaceHost;
187       if (hdfFaceAuthInterfaceHost == nullptr) {
188           IAM_LOGE("%{public}s: failed to create HdfFaceAuthInterfaceHost object", __func__);
189           return HDF_FAILURE;
190       }
191
192       hdfFaceAuthInterfaceHost->ioService.Dispatch = FaceAuthInterfaceDriverDispatch;
193       hdfFaceAuthInterfaceHost->ioService.Open = NULL;
194       hdfFaceAuthInterfaceHost->ioService.Release = NULL;
195
196       auto serviceImpl = IFaceAuthInterface::Get(true);
197       if (serviceImpl == nullptr) {
198           IAM_LOGE("%{public}s: failed to implement service", __func__);
199           return HDF_FAILURE;
200       }
201
202       hdfFaceAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
203           IFaceAuthInterface::GetDescriptor());
204       if (hdfFaceAuthInterfaceHost->stub == nullptr) {
205           IAM_LOGE("%{public}s: failed to get stub object", __func__);
206           return HDF_FAILURE;
207       }
208
209       deviceObject->service = &hdfFaceAuthInterfaceHost->ioService;
210       IAM_LOGI("success");
211       return HDF_SUCCESS;
212   }
213
214   // 释放Face_auth驱动中的资源
215   void HdfFaceAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject)
216   {
217       IAM_LOGI("start");
218       auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(deviceObject->service,
219           struct HdfFaceAuthInterfaceHost, ioService);
220       delete hdfFaceAuthInterfaceHost;
221       IAM_LOGI("success");
222   }
223
224   // 注册Face_auth驱动入口数据结构体对象
225   struct HdfDriverEntry g_faceAuthInterfaceDriverEntry = {
226       .moduleVersion = 1,
227       .moduleName = "faceauth_interface_service",
228       .Bind = HdfFaceAuthInterfaceDriverBind,
229       .Init = HdfFaceAuthInterfaceDriverInit,
230       .Release = HdfFaceAuthInterfaceDriverRelease,
231   };
232
233   // 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出
234   HDF_INIT(g_faceAuthInterfaceDriverEntry);
235   ```
236
2372. 实现获取执行器列表接口,详细代码参见[face_auth_interface_service.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_service.cpp)文件。
238
239   ```c++
240   // 执行器实现类
241   class ExecutorImpl : public IExecutor {
242   public:
243       ExecutorImpl(struct ExecutorInfo executorInfo);
244       virtual ~ExecutorImpl() {}
245
246   private:
247       struct ExecutorInfo executorInfo_; // 执行器信息
248   };
249
250   static constexpr uint16_t SENSOR_ID = 123; // 执行器sensorID
251   static constexpr uint32_t EXECUTOR_TYPE = 123; // 执行器类型
252   static constexpr size_t PUBLIC_KEY_LEN = 32; // 执行器32字节公钥
253
254   // 创建HDI服务对象
255   extern "C" IFaceAuthInterface *FaceAuthInterfaceImplGetInstance(void)
256   {
257       auto faceAuthInterfaceService = new (std::nothrow) FaceAuthInterfaceService();
258       if (faceAuthInterfaceService == nullptr) {
259           IAM_LOGE("faceAuthInterfaceService is nullptr");
260           return nullptr;
261       }
262       return faceAuthInterfaceService;
263   }
264
265   // 获取执行器列表实现,创建执行器
266   int32_t GetExecutorList(std::vector<sptr<IExecutor>>& executorList)
267   {
268       IAM_LOGI("interface mock start");
269       executorList.clear();
270       struct ExecutorInfo executorInfoExample = {
271           .sensorId = SENSOR_ID,
272           .executorType = EXECUTOR_TYPE,
273           .executorRole = ExecutorRole::ALL_IN_ONE,
274           .authType = AuthType::FACE,
275           .esl = ExecutorSecureLevel::ESL0, // ExecutorSecureLevel标识执行器的安全等级,范围是ESL0~ESL3,其中ESL3标识的安全等级最高
276           .publicKey = std::vector<uint8_t>(PUBLIC_KEY_LEN, 0), // 32字节公钥,算法是Ed25519
277           .extraInfo = {},
278       };
279       auto executor = new (std::nothrow) ExecutorImpl(executorInfoExample);
280       if (executor == nullptr) {
281           IAM_LOGE("executor is nullptr");
282           return HDF_FAILURE;
283       }
284       executorList.push_back(sptr<IExecutor>(executor));
285       IAM_LOGI("interface mock success");
286       return HDF_SUCCESS;
287   }
288   ```
289
2903. 实现执行器每个功能接口,详细代码参见[executor_impl.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/executor_impl.cpp)文件。
291
292   ```c++
293   // 实现获取执行器信息接口
294   int32_t GetExecutorInfo(ExecutorInfo& info)
295   {
296       IAM_LOGI("interface mock start");
297       info = executorInfo_;
298       IAM_LOGI("get executor information success");
299       return HDF_SUCCESS;
300   }
301
302   // 实现获取指定模板ID的模板信息接口
303   int32_t GetTemplateInfo(uint64_t templateId, TemplateInfo& info)
304   {
305       IAM_LOGI("interface mock start");
306       static_cast<void>(templateId);
307       info = {0};
308       IAM_LOGI("get template information success");
309       return HDF_SUCCESS;
310   }
311
312   // 实现执行器注册成功后,获取用户认证框架的公钥信息、获取用户认证框架的模板列表接口。将公钥信息保持,模板列表用于和本地的模板做对账
313   int32_t OnRegisterFinish(const std::vector<uint64_t>& templateIdList,
314       const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo)
315   {
316       IAM_LOGI("interface mock start");
317       static_cast<void>(templateIdList);
318       static_cast<void>(extraInfo);
319       static_cast<void>(frameworkPublicKey);
320       IAM_LOGI("register finish");
321       return HDF_SUCCESS;
322   }
323
324   // 实现人脸录入接口
325   int32_t Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,
326       const sptr<IExecutorCallback>& callbackObj)
327   {
328       IAM_LOGI("interface mock start");
329       static_cast<void>(scheduleId);
330       static_cast<void>(extraInfo);
331       IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);
332       int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});
333       if (ret != ResultCode::SUCCESS) {
334           IAM_LOGE("callback result is %{public}d", ret);
335           return HDF_FAILURE;
336       }
337       return HDF_SUCCESS;
338   }
339
340   // 实现人脸认证接口
341   int32_t Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList,
342       const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj)
343   {
344       IAM_LOGI("interface mock start");
345       static_cast<void>(scheduleId);
346       static_cast<void>(templateIdList);
347       static_cast<void>(extraInfo);
348       IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED);
349       int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {});
350       if (ret != ResultCode::SUCCESS) {
351           IAM_LOGE("callback result is %{public}d", ret);
352           return HDF_FAILURE;
353       }
354       return HDF_SUCCESS;
355   }
356
357   // 实现人脸识别接口
358   int32_t Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo,
359       const sptr<IExecutorCallback>& callbackObj)
360   {
361       IAM_LOGI("interface mock start");
362       static_cast<void>(scheduleId);
363       static_cast<void>(extraInfo);
364       IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);
365       int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});
366       if (ret != ResultCode::SUCCESS) {
367           IAM_LOGE("callback result is %{public}d", ret);
368           return HDF_FAILURE;
369       }
370       return HDF_SUCCESS;
371   }
372
373   // 实现删除人脸模板接口
374   int32_t Delete(const std::vector<uint64_t>& templateIdList)
375   {
376       IAM_LOGI("interface mock start");
377       static_cast<void>(templateIdList);
378       IAM_LOGI("delete success");
379       return HDF_SUCCESS;
380   }
381
382   // 实现通过scheduleId取消指定操作接口
383   int32_t Cancel(uint64_t scheduleId)
384   {
385       IAM_LOGI("interface mock start");
386       static_cast<void>(scheduleId);
387       IAM_LOGI("cancel success");
388       return HDF_SUCCESS;
389   }
390
391   // 实现人脸认证服务向Face_auth驱动传递参数的通用接口,当前需要实现冻结与解锁模板命令
392   int32_t SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo,
393       const sptr<IExecutorCallback>& callbackObj)
394   {
395       IAM_LOGI("interface mock start");
396       static_cast<void>(extraInfo);
397       int32_t ret;
398       switch (commandId) {
399           case LOCK_TEMPLATE:
400               IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS);
401               ret = callbackObj->OnResult(ResultCode::SUCCESS, {});
402               if (ret != ResultCode::SUCCESS) {
403                   IAM_LOGE("callback result is %{public}d", ret);
404                   return HDF_FAILURE;
405               }
406               break;
407           case UNLOCK_TEMPLATE:
408               IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS);
409               ret = callbackObj->OnResult(ResultCode::SUCCESS, {});
410               if (ret != ResultCode::SUCCESS) {
411                   IAM_LOGE("callback result is %{public}d", ret);
412                   return HDF_FAILURE;
413               }
414               break;
415           default:
416               IAM_LOGD("not support CommandId : %{public}d", commandId);
417               ret = callbackObj->OnResult(ResultCode::GENERAL_ERROR, {});
418               if (ret != ResultCode::SUCCESS) {
419                   IAM_LOGE("callback result is %{public}d", ret);
420                   return HDF_FAILURE;
421               }
422       }
423       return HDF_SUCCESS;
424   }
425   ```
426
4274. 用户身份认证框架支持多driver,当增加driver或者修改driver信息,需要修改如下文件中serviceName2Config。
428
429   ```c++
430   // base/user_iam/face_auth/services/src/face_auth_service.cpp
431   void FaceAuthService::StartDriverManager()
432   {
433       IAM_LOGI("start");
434       // 此处增加或修改driver服务名字和ID,driver服务名字和ID需要全局唯一
435       const std::map<std::string, UserAuth::ServiceConfig> serviceName2Config = {
436           {"face_auth_interface_service", {1, std::make_shared<FaceAuthDriverHdi>()}},
437       };
438       UserIAM::UserAuth::IDriverManager::GetInstance().Start(serviceName2Config);
439   }
440   ```
441
442### 调测验证
443
444驱动开发完成后,通过[用户认证API接口](../../application-dev/reference/apis/js-apis-useriam-userauth.md)开发JS应用,基于Hi3516DV300平台验证。认证和取消功能验证的JS测试代码如下:
445
446    ```js
447    // API version 9
448    import userIAM_userAuth from '@ohos.userIAM.userAuth';
449
450    let challenge = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
451    let authType = userIAM_userAuth.UserAuthType.FACE;
452    let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1;
453
454    // 获取认证对象
455    let auth;
456    try {
457        auth = userIAM_userAuth.getAuthInstance(challenge, authType, authTrustLevel);
458        console.log("get auth instance success");
459    } catch (error) {
460        console.log("get auth instance failed" + error);
461    }
462
463    // 订阅认证结果
464    try {
465        auth.on("result", {
466            callback: (result: userIAM_userAuth.AuthResultInfo) => {
467                console.log("authV9 result " + result.result);
468                console.log("authV9 token " + result.token);
469                console.log("authV9 remainAttempts " + result.remainAttempts);
470                console.log("authV9 lockoutDuration " + result.lockoutDuration);
471            }
472        });
473        console.log("subscribe authentication event success");
474    } catch (error) {
475        console.log("subscribe authentication event failed " + error);
476    }
477
478    // 开始认证
479    try {
480        auth.start();
481        console.info("authV9 start auth success");
482    } catch (error) {
483        console.info("authV9 start auth failed, error = " + error);
484    }
485
486    // 取消认证
487    try {
488        auth.cancel();
489        console.info("cancel auth success");
490    } catch (error) {
491        console.info("cancel auth failed, error = " + error);
492    }
493
494    // 取消订阅认证结果
495    try {
496        auth.off("result");
497        console.info("cancel subscribe authentication event success");
498    } catch (error) {
499        console.info("cancel subscribe authentication event failed, error = " + error);
500    }
501    ```
502