Passkeys Chrome 插件 Wallet
Chrome 插件能否使用 passkeys
是的,Chrome 插件(扩展程序)可以使用 Passkeys。Passkeys 是一种基于公钥加密的身份验证方法,旨在替代传统的密码。它们通常与 WebAuthn(Web Authentication API)一起使用,WebAuthn 是 FIDO2 规范的一部分。
如果你正在开发一个 Chrome 插件,并希望它能够使用 Passkeys 进行身份验证,你可以利用 WebAuthn API 来实现这一功能。以下是一个简要的步骤概述:
-
设置 WebAuthn 环境: 确保你的服务器和前端环境支持 WebAuthn。你需要一个安全的(HTTPS)连接,因为 WebAuthn 只能在安全的上下文中使用。
-
生成和注册 Passkey:
- 当用户注册时,使用 WebAuthn API 生成一个新的公钥/私钥对。
- 将公钥发送到服务器并存储在用户的账户信息中。
-
验证 Passkey:
- 当用户登录时,使用 WebAuthn API 进行身份验证。
- 服务器会发送一个挑战(challenge)给客户端,客户端使用私钥签名挑战并返回签名。
- 服务器验证签名是否正确,从而验证用户的身份。
以下是一个简单的示例代码,展示如何在 Chrome 插件中使用 WebAuthn API:
// content_script.js
async function registerPasskey() {
const publicKey = {
challenge: new Uint8Array(32),
rp: {
name: 'Example',
},
user: {
id: new Uint8Array(16),
name: 'user@example.com',
displayName: 'User Example',
},
pubKeyCredParams: [
{
type: 'public-key',
alg: -7,
},
],
};
try {
const credential = await navigator.credentials.create({ publicKey });
console.log('Credential created:', credential);
// Send the credential to the server for registration
} catch (err) {
console.error('Error creating credential:', err);
}
}
async function authenticatePasskey() {
const publicKey = {
challenge: new Uint8Array(32),
allowCredentials: [
{
id: new Uint8Array(16), // The credential ID from registration
type: 'public-key',
},
],
};
try {
const assertion = await navigator.credentials.get({ publicKey });
console.log('Assertion:', assertion);
// Send the assertion to the server for verification
} catch (err) {
console.error('Error getting assertion:', err);
}
}
// Example usage
registerPasskey();
authenticatePasskey();
请注意,这只是一个简化的示例,实际应用中需要更多的错误处理和安全措施。此外,你还需要在服务器端实现相应的逻辑来处理 Passkey 的注册和验证。
通过这种方式,Chrome 插件可以利用 Passkeys 提供更安全和方便的身份验证体验。
给个完整 demo 我测试下
好的,以下是一个完整的示例,展示如何在 Chrome 插件中使用 Passkeys 进行注册和验证。这个示例包括一个简单的服务器端实现和 Chrome 插件的前端代码。
1. 服务器端代码(Node.js + Express)
首先,创建一个简单的服务器来处理注册和验证请求。你需要安装 express 和 body-parser 包。
npm install express body-parser
创建一个名为 server.js 的文件,并添加以下代码:
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
app.use(bodyParser.json());
let users = {}; // In-memory user storage for demo purposes
// Utility function to generate challenge
function generateChallenge() {
return crypto.randomBytes(32).toString('base64');
}
// Registration endpoint
app.post('/register', (req, res) => {
const { username, displayName } = req.body;
const userId = crypto.randomBytes(16).toString('base64');
const challenge = generateChallenge();
users[username] = {
id: userId,
displayName: displayName,
challenge: challenge,
credentials: [],
};
const publicKey = {
challenge: Buffer.from(challenge, 'base64'),
rp: {
name: 'Example',
},
user: {
id: Buffer.from(userId, 'base64'),
name: username,
displayName: displayName,
},
pubKeyCredParams: [
{
type: 'public-key',
alg: -7,
},
],
};
res.json(publicKey);
});
// Verification endpoint
app.post('/verify', (req, res) => {
const { username, credential } = req.body;
const user = users[username];
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const expectedChallenge = user.challenge;
const receivedChallenge = credential.response.clientDataJSON.challenge;
if (expectedChallenge !== receivedChallenge) {
return res.status(400).json({ error: 'Challenges do not match' });
}
// Simplified verification for demo purposes
user.credentials.push(credential);
res.json({ success: true });
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
2. Chrome 插件代码
创建一个 Chrome 插件目录,并在其中创建以下文件:
manifest.json
{
"manifest_version": 3,
"name": "Passkey Demo",
"version": "1.0",
"permissions": ["activeTab"],
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
}
}
popup.html
<!DOCTYPE html>
<html>
<head>
<title>Passkey Demo</title>
<script src="popup.js"></script>
</head>
<body>
<h1>Passkey Demo</h1>
<button id="register">Register</button>
<button id="authenticate">Authenticate</button>
</body>
</html>
popup.js
document.getElementById('register').addEventListener('click', async () => {
const username = 'user@example.com';
const displayName = 'User Example';
const response = await fetch('http://localhost:3000/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, displayName }),
});
const publicKey = await response.json();
try {
const credential = await navigator.credentials.create({ publicKey });
console.log('Credential created:', credential);
await fetch('http://localhost:3000/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
credential,
}),
});
} catch (err) {
console.error('Error creating credential:', err);
}
});
document.getElementById('authenticate').addEventListener('click', async () => {
const username = 'user@example.com';
const response = await fetch('http://localhost:3000/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
});
const publicKey = await response.json();
try {
const assertion = await navigator.credentials.get({ publicKey });
console.log('Assertion:', assertion);
await fetch('http://localhost:3000/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
credential: assertion,
}),
});
} catch (err) {
console.error('Error getting assertion:', err);
}
});
background.js
chrome.runtime.onInstalled.addListener(() => {
console.log('Passkey Demo extension installed');
});
3. 运行和测试
-
启动服务器: 在终端中运行
node server.js启动服务器。 -
加载 Chrome 插件:
- 打开 Chrome 浏览器,进入
chrome://extensions/。 - 打开右上角的“开发者模式”。
- 点击“加载已解压的扩展程序”,选择你的插件目录。
- 打开 Chrome 浏览器,进入
-
测试注册和验证:
- 点击浏览器工具栏中的插件图标,打开插件的弹出页面。
- 点击“Register”按钮进行注册。
- 点击“Authenticate”按钮进行验证。
请注意,这个示例只是一个基础的演示,实际应用中需要更多的安全措施和错误处理。