首页  >  文章  >  web前端  >  使用 WebAuthn 在 Angular 应用程序中集成指纹和 Face ID 身份验证:分步指南

使用 WebAuthn 在 Angular 应用程序中集成指纹和 Face ID 身份验证:分步指南

王林
王林原创
2024-09-05 19:00:21832浏览

说实话,我们都希望能够使用指纹或 Face ID 登录网站,就像在移动应用程序上一样,对吗?好吧,多亏了网络生物识别技术,这个梦想不再那么遥不可及。想象一下,抛弃那些又长又复杂的密码,只需使用我们的指纹或面部登录我们最喜欢的网站。听起来很酷,不是吗?

Integrate Fingerprint and Face ID Authentication in Your Angular App Using WebAuthn: A Step-by-Step Guide

由 WebAuthn 提供支持的网络生物识别技术使这一切成为可能。这是一个非常简单的东西的奇特名称:使用与手机指纹传感器或面部识别相同的安全性来验证我们的身份,但直接在我们的网络浏览器中进行。不再担心密码泄露或被盗——只需快速扫描,我们就可以了。

在本教程中,我们将亲自动手将指纹和 Face ID 登录集成到我们的 Angular 应用程序中。我们将介绍基本知识,例如 WebAuthn API 的工作原理以及我们需要在后端执行哪些操作以确保一切安全和顺利。这比您想象的要容易,到最后,我们将为未来的身份验证做好所有设置。那么,让我们开始吧,让登录变得轻而易举!

了解 WebAuthn:Angular 应用程序中指纹和人脸识别的基础知识

Integrate Fingerprint and Face ID Authentication in Your Angular App Using WebAuthn: A Step-by-Step Guide

好吧,在我们进入代码之前,让我们快速了解一下 WebAuthn 的含义。将 WebAuthn 视为桥梁,将我们的应用程序与我们喜欢的手机上的生物识别功能(如指纹和 Face ID)连接起来,就在我们的浏览器中。它使用公钥加密技术来验证用户身份,这意味着不再存储黑客可以轻松窃取的普通旧密码。相反,我们谈论的是安全生成的密钥,它使我们的登录既安全又无缝。

关键对象及其作用

为了让事情顺利进行,我们需要了解 WebAuthn 游戏中的几个关键角色:PublicKeyCredentialCreationOptions 和 PublicKeyCredentialRequestOptions。不要让长名称吓到您——它们只是告诉浏览器我们要如何注册和验证用户身份的奇特方式。

1. 公钥凭证创建选项

这是我们设置新用户凭据时的首选对象。它包括:

  • 挑战:服务器生成的唯一随机值,以确保响应是最新的且无法重复使用。
  • rp:代表依赖方(我们的应用程序),包含应用程序名称和 ID 等详细信息。
  • 用户:有关用户的信息,例如唯一 ID、用户名和显示名称。
  • pubKeyCredParams:我们允许的公钥算法列表。
  • authenticatorSelection:帮助我们根据附件类型(平台或跨平台)和用户验证级别等选择正确的身份验证器类型。

2. 公钥凭证请求选项

当需要验证我们的用户时,这个对象就会成为人们关注的焦点。它包括:

  • 挑战:就像以前一样,这确保我们的身份验证请求是新鲜且独特的。
  • allowCredentials:指定允许用户使用哪些凭据。
  • userVerification:指示是否需要用户验证(如指纹扫描)。

有了这些对象,我们的 Angular 应用程序将能够指导用户注册其生物识别数据并快速、安全地进行身份验证。接下来,我们将进入代码,看看如何在我们的应用程序中实现这个魔法!

设置 Angular 应用程序

在本节中,我们将指导您使用 WebAuthn 设置具有生物识别身份验证的 Angular 应用程序。我们将专注于使用指纹和 Face ID,所以让我们动手吧!

第 1 步:设置我们的 Angular 项目

首先,让我们创建一个新的 Angular 项目。打开终端并输入以下命令:

ng new web-biometrics-demo
cd web-biometrics-demo
ng serve

这会设置一个基本的 Angular 应用程序,运行 ngserve 将在 http://localhost:4200/ 上启动您的应用程序。您应该看到默认的 Angular 欢迎页面。现在,我们已准备好集成 WebAuthn 进行生物识别身份验证。

第 2 步:创建 WebAuthn 服务

我们需要 Angular 中的一个服务来管理我们所有的 WebAuthn 功能,包括使用生物识别技术进行注册和身份验证。让我们通过运行以下命令来创建此服务:

ng generate service services/webauthn

Now, open webauthn.service.ts and add the following code:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class WebAuthnService {

  constructor() { }

  // Generates a random buffer to use as a challenge, which is a unique value needed for security
  private generateRandomBuffer(length: number): Uint8Array {
    const randomBuffer = new Uint8Array(length);
    window.crypto.getRandomValues(randomBuffer); // Fills the buffer with cryptographically secure random values
    return randomBuffer;
  }

  // Registers a new credential (like a fingerprint or Face ID) for the user
  async register() {
    // Generate a unique challenge for the registration process
    const challenge = this.generateRandomBuffer(32);

    // PublicKeyCredentialCreationOptions is the core object needed for registration
    const publicKey: PublicKeyCredentialCreationOptions = {
      challenge: challenge, // A random value generated by the server to ensure the request is fresh and unique
      rp: { // Relying Party (your app) information
        name: "OurAwesomeApp" // Display name of your app
      },
      user: { // User information
        id: this.generateRandomBuffer(16), // A unique identifier for the user
        name: "user@example.com", // User's email or username
        displayName: "User Example" // A friendly name for the user
      },
      pubKeyCredParams: [{ // Array of acceptable public key algorithms
        type: "public-key",
        alg: -7  // Represents the ES256 algorithm (Elliptic Curve Digital Signature Algorithm)
      }],
      authenticatorSelection: { // Criteria for selecting the appropriate authenticator
        authenticatorAttachment: "platform", // Ensures we use the device's built-in biometric authenticator like Touch ID or Face ID
        userVerification: "required" // Requires user verification (e.g., fingerprint or face scan)
      },
      timeout: 60000, // Timeout for the registration operation in milliseconds
      attestation: "direct" // Attestation provides proof of the authenticator's properties and is sent back to the server
    };

    try {
      // This will prompt the user to register their biometric credential
      const credential = await navigator.credentials.create({ publicKey }) as PublicKeyCredential;
      this.storeCredential(credential, challenge); // Store the credential details locally for demo purposes
      console.log("Registration successful!", credential);
      return credential; // Return the credential object containing the user's public key and other details
    } catch (err) {
      console.error("Registration failed:", err);
      throw err; // Handle any errors that occur during registration
    }
  }

  // Authenticates the user with stored credentials (like a fingerprint or Face ID)
  async authenticate() {
    const storedCredential = this.getStoredCredential(); // Retrieve stored credential information
    if (!storedCredential) {
      throw new Error("No stored credential found. Please register first."); // Error if no credentials are found
    }

    // PublicKeyCredentialRequestOptions is used to prompt the user to authenticate
    const publicKey: PublicKeyCredentialRequestOptions = {
      challenge: new Uint8Array(storedCredential.challenge), // A new challenge to ensure the request is fresh and unique
      allowCredentials: [{ // Specifies which credentials can be used for authentication
        id: new Uint8Array(storedCredential.rawId), // The ID of the credential to use
        type: "public-key"
      }],
      userVerification: "required", // Requires user verification (e.g., fingerprint or face scan)
      timeout: 60000 // Timeout for the authentication operation in milliseconds
    };

    try {
      // This will prompt the user to authenticate using their registered biometric credential
      const credential = await navigator.credentials.get({ publicKey }) as PublicKeyCredential;
      console.log("Authentication successful!", credential);
      return credential; // Return the credential object with authentication details
    } catch (err) {
      console.error("Authentication failed:", err);
      throw err; // Handle any errors that occur during authentication
    }
  }

  // Stores credential data in localStorage (for demo purposes only; this should be handled securely in production)
  private storeCredential(credential: PublicKeyCredential, challenge: Uint8Array) {
    const credentialData = {
      rawId: Array.from(new Uint8Array(credential.rawId)), // Converts the raw ID to an array for storage
      challenge: Array.from(challenge) // Converts the challenge to an array for storage
    };
    localStorage.setItem('webauthn_credential', JSON.stringify(credentialData)); // Store the data as a JSON string
  }

  // Retrieves stored credential data from localStorage
  private getStoredCredential(): any {
    const storedCredential = localStorage.getItem('webauthn_credential');
    return storedCredential ? JSON.parse(storedCredential) : null; // Parse the stored JSON back into an object
  }
}

What’s Happening in the Code?

  • generateRandomBuffer: Creates a random buffer that serves as a challenge to ensure each authentication or registration request is unique.

  • register: This method sets up the biometric registration process. It uses PublicKeyCredentialCreationOptions to define parameters like the challenge, relying party (your app), user information, and acceptable public key algorithms. When navigator.credentials.create() is called, the browser prompts the user to register their biometric data.

  • authenticate: This method handles user authentication with biometrics. It uses PublicKeyCredentialRequestOptions to define the authentication challenge and credentials that can be used. The method prompts the user to authenticate with their registered biometrics.

  • storeCredential and getStoredCredential: These methods handle storing and retrieving credentials in localStorage for demonstration purposes.

    In a real-world app, you’d securely store this information on your backend.

Step 3: Building the UI

Now, let’s create a basic UI with buttons to trigger the registration and login functions. This UI will provide feedback based on whether the registration or login was successful.

Open app.component.ts and replace the content with the following:

import { Component } from '@angular/core';
import { WebAuthnService } from './services/webauthn.service';

@Component({
  selector: 'app-root',
  template: `
    <div class="auth-container">
      <h1>Web Biometrics in Angular</h1>
      <button (click)="register()">Register with Fingerprint</button>
      <button (click)="login()">Login with Face ID</button>
      <p *ngIf="message" [ngClass]="{'success': isSuccess, 'error': !isSuccess}">{{ message }}</p>
    </div>
  `,
  styles: [`
    .auth-container {
      text-align: center;
      padding: 50px;
    }
    .success {
      color: green;
    }
    .error {
      color: red;
    }
    button {
      margin: 10px;
      padding: 10px 20px;
      font-size: 16px;
    }
    p {
      margin: 10px;
      font-size: 16px;
    }
  `]
})
export class AppComponent {
  message: string | null = null; // Message to display feedback to the user
  isSuccess: boolean = false; // Indicates if the last action was successful

  constructor(private webAuthnService: WebAuthnService) { }

  // Trigger registration process and update the UI based on the outcome
  async register() {
    try {
      await this.webAuthnService.register();
      this.message = "Registration successful!"; // Success message if registration works
      this.isSuccess = true;
    } catch (err) {
      this.message = "Registration failed. Please try again."; // Error message if something goes wrong
      this.isSuccess = false;
    }
  }

  // Trigger authentication process and update the UI based on the outcome
  async login() {
    try {
      await this.webAuthnService.authenticate();
      this.message = "Authentication successful!"; // Success message if authentication works
      this.isSuccess = true;
    } catch (err) {
      this.message = "Authentication failed. Please try again."; // Error message if something goes wrong
      this.isSuccess = false;
    }
  }
}

What’s Happening in the Component?

register and login methods: These methods call the respective register and authenticate methods from the WebAuthnService. If successful, a success message is displayed; otherwise, an error message is shown.

Template and Styling: The template includes buttons to trigger registration and login, and it displays messages to the user based on the operation's outcome. The buttons are styled for simplicity.

That’s it! We’ve built a basic Angular app with WebAuthn-based biometric authentication, supporting fingerprints and Face ID. This setup captures the core concepts and lays a foundation that can be expanded with additional features and security measures for a production environment.

Backend Considerations

When implementing biometric authentication like fingerprints or Face ID in web applications using WebAuthn, the backend plays a crucial role in managing the security and flow of data. Here’s a breakdown of how the backend processes work in theory, focusing on registration and login functionalities.

Registration: Sign-up

1. User Registration Flow:

  • User Data Capture: During registration, the user provides basic credentials, such as an email and password. If biometric data is also being registered, this is captured as part of the WebAuthn response.

  • Password Hashing: For security, passwords are never stored in plain text. Instead, they are hashed using a library like bcrypt before being stored in the database.

  • Storing WebAuthn Credentials:

    • Challenge Handling: The server sends a challenge during the registration process, which is a randomly generated value to prevent replay attacks.
    • Response Validation: When the client responds with the WebAuthn data, it includes clientDataJSON and attestationObject that need to be decoded and verified.
    • Credential Storage: After validation, key data from the response—like the webauthnId (a unique identifier for the credential) and the publicKey (used to verify future authentications)—are stored in the database alongside the user record.

2. Backend Code Responsibilities:

  • The backend uses libraries like cbor to decode binary data formats from the WebAuthn response, extracting necessary elements like the public key and authenticator data.

  • It ensures that the challenge from the initial registration request matches what is returned in the WebAuthn response to verify the authenticity of the registration.

  • If the WebAuthn response passes all checks, the credentials are saved in the database, linked to the user account.

Login

1. User Login Flow:

  • Challenge Generation: Similar to registration, the server generates a challenge that must be responded to by the client’s authenticator during login.

  • Validating the WebAuthn Response:

    • 客户端发回一个包含对质询的响应的 PublicKeyCredentialRequestOptions 对象。
    • 后端解码并验证此响应,确保质询和凭据与数据库中存储的内容匹配。
  • 凭证验证:

    • 注册期间存储的公钥用于验证登录响应中的签名。
    • 如果凭据匹配,后端将允许登录并为会话生成身份验证令牌(如 JWT)。

错误处理:

  • 不匹配或无效响应:如果质询响应与预期值不匹配,或者 WebAuthn 凭据未正确验证,后端会返回错误,以防止未经授权的访问。

  • 回退到密码:如果 WebAuthn 失败或不可用,系统可以恢复到传统的密码验证,确保用户仍然可以访问其帐户。

安全考虑

  • 数据完整性:WebAuthn 凭据的完整性至关重要。任何存储或传输的修改都会导致验证失败,从而确保身份验证过程的安全。

  • 挑战随机数:使用独特的、有时间限制的挑战可确保响应无法重复使用,从而防止重放攻击。

  • 公钥存储:仅存储公钥(不能用于模拟用户)可以增强安全性,因为私钥保留在客户端设备上。

通过遵循这些原则,后端可以有效地管理生物识别身份验证,确保为想要在 Angular 应用中使用指纹或 Face ID 等功能的用户提供安全、无缝的体验。

总之

在本教程中,我们介绍了使用 WebAuthn 将生物识别身份验证与 Angular 集成。我们涵盖了基础知识,从理解关键的 WebAuthn 对象(如 PublicKeyCredentialCreationOptions 和 PublicKeyCredentialRequestOptions)到设置 Angular 服务和 UI 组件以实现顺利的注册和登录过程。我们还讨论了安全处理生物识别身份验证所需的后端注意事项。

对于那些渴望看到 WebAuthn 实际应用的人,我提供了一个演示和一个具有完整实现的存储库。您可以在此处查看演示,并在 GitHub 上的此存储库中探索源代码。

采用生物识别身份验证不仅可以增强安全性,还可以简化用户体验,为未来像指纹扫描或快速面部识别一样轻松的登录铺平道路。当您将这些功能集成到 Angular 应用程序中时,您将为构建更安全、更用户友好的网络做出贡献。快乐编码!

以上是使用 WebAuthn 在 Angular 应用程序中集成指纹和 Face ID 身份验证:分步指南的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn