Let’s be honest—we’ve all wished we could log into websites using our fingerprints or Face ID, just like on mobile apps, right? Well, thanks to web biometrics, that dream isn’t so far-fetched anymore. Imagine ditching those long, complicated passwords and simply using our fingerprint or face to sign in to our favorite websites. Sounds cool, doesn’t it?
Web biometrics, powered by WebAuthn, is making this possible. It's a fancy name for something quite simple: authenticating us with the same kind of security as our phone’s fingerprint sensor or facial recognition, but directly in our web browser. No more worrying about passwords getting leaked or stolen—just a quick scan, and we’re in.
In this tutorial, we’re going to get hands-on with integrating fingerprint and Face ID login into our Angular apps. We’ll cover the essentials, like how the WebAuthn API works and what we need to do on the backend to keep everything secure and smooth. It’s easier than you might think, and by the end, we’ll have our app all set up for the future of authentication. So, let’s dive in and make logging in a breeze!
Alright, before we jump into the code, let’s get a quick handle on what WebAuthn is all about. Think of WebAuthn as the bridge that connects our apps to the cool biometric features we love on our phones—like fingerprints and Face ID—right in our browsers. It uses public key cryptography to authenticate users, which means no more storing plain old passwords that hackers can easily snatch up. Instead, we’re talking about securely generated keys that make our logins both safe and seamless.
To get things rolling, we need to understand a couple of key players in the WebAuthn game: PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions. Don’t let the long names scare you—they’re just fancy ways to tell the browser how we want to register and authenticate users.
This is our go-to object when setting up new user credentials. It includes:
When it’s time to verify our users, this object takes the spotlight. It includes:
With these objects in hand, our Angular app will be able to guide users through registering their biometric data and authenticating quickly and securely. Next, we’ll get into the code and see how to make this magic happen in our app!
In this section, we’ll guide you through setting up an Angular application with biometric authentication using WebAuthn. We’ll focus on using fingerprints and Face ID, so let's get our hands dirty!
To get started, let’s create a new Angular project. Open your terminal and type the following commands:
ng new web-biometrics-demo cd web-biometrics-demo ng serve
This sets up a basic Angular application, and running ng serve will start your app on http://localhost:4200/. You should see the default Angular welcome page. Now, we’re ready to integrate WebAuthn for biometric authentication.
We need a service in Angular to manage all our WebAuthn functionality, including registration and authentication using biometrics. Let’s create this service by running:
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 } }
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.
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; } } }
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.
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.
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:
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.
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:
資格証明:
不一致または無効な応答: チャレンジ応答が期待値と一致しない場合、または WebAuthn 資格情報が正しく検証されない場合、バックエンドはエラーで応答し、不正アクセスを防ぎます。
パスワードへのフォールバック: WebAuthn が失敗するか利用できない場合、システムは従来のパスワード検証に戻すことができ、ユーザーは引き続きアカウントにアクセスできるようになります。
データの整合性: WebAuthn 認証情報の整合性は重要です。ストレージまたは送信に変更が加えられると検証が失敗するため、認証プロセスが安全になります。
チャレンジ ノンス: 独自の時間制限付きチャレンジを使用することで、応答が再利用できないようになり、リプレイ攻撃から保護されます。
公開キーのストレージ: 公開キー (ユーザーになりすますために使用することはできません) のみを保存すると、秘密キーがクライアント デバイスに残るため、セキュリティが強化されます。
これらの原則に従うことで、バックエンドは生体認証を効果的に管理し、Angular アプリで指紋や Face ID などの機能を使用したいユーザーに安全でシームレスなエクスペリエンスを保証します。
このチュートリアルでは、WebAuthn を使用して生体認証を Angular と統合する方法について説明しました。 PublicKeyCredentialCreationOptions や PublicKeyCredentialRequestOptions などの主要な WebAuthn オブジェクトの理解から、登録とログインのプロセスをスムーズにするための Angular サービスと UI コンポーネントの設定まで、基本事項を説明しました。また、生体認証を安全に処理するために必要なバックエンドの考慮事項についても説明しました。
WebAuthn の動作を確認したい方のために、完全な実装を備えたデモとリポジトリを提供しました。ここでデモをチェックアウトしたり、GitHub のこのリポジトリでソース コードを探索したりできます。
生体認証の導入はセキュリティを強化するだけでなく、ユーザーエクスペリエンスを簡素化し、指紋スキャンや素早い顔認識と同じくらい簡単にログインできる未来への道を切り開きます。これらの機能を Angular アプリに統合すると、より安全でユーザー フレンドリーな Web に貢献することになります。コーディングを楽しんでください!
The above is the detailed content of Integrate Fingerprint and Face ID Authentication in Your Angular App Using WebAuthn: A Step-by-Step Guide. For more information, please follow other related articles on the PHP Chinese website!