注意: 此项功能仅在安全上下文 (HTTPS) 及支持的浏览器中可用。
Web Authentication API 继承自 Credential Management API,使用公钥密码学赋能强身份验证,不需要手机短信就能实现无密码验证和安全的双因素验证。
Web Authentication API(也称作 WebAuthn)使用非对称(公钥)加密替代密码或短信进行网站注册、身份验证以及双因素验证,其优点包括:
许多网站已经包含了用户注册、登录的页面,而 Web Authentication API 则能够取代这些已有网页,或作为补充。与其他的 Credential Management API 形式相类似,Web Authentication API 的两个基本方法分别对应于注册和登录:
注意:create() 和 get() 都要求安全上下文(即服务器通过 HTTPS 连接或为本地服务器),而且如果浏览器没有在安全上下文中运作,也无法使用。
在基础实现中,create() 和 get() 会从服务器接收一个大随机数,称为挑战,用私钥签名后将挑战返回服务器。这个过程向服务器证明了用户拥有身份验证所需要的私钥,且没有任何密码在网络上传输。
为了了解 create() 和 get() 方法的实际应用,我们首先需要理解这两个方法处在浏览器之外的以下两个部分之间:
注册过程通常包含六个步骤,图 1 所示是对注册过程所需数据的简要概述。创建注册请求所需的字段、可选字段及其意义的完整信息可查看 PublicKeyCredentialCreationOptions 字典。同样,所有的响应字段可以在 PublicKeyCredential 接口中查看(其中 PublicKeyCredential.response 是 AuthenticatorAttestationResponse 接口)。注意,大部分 JavaScript 程序员在创建应用程序时只会关注步骤 1 和 5,也就是调用 create() 函数与返回。但是,步骤 2、3、4 对于了解浏览器和认证器中的处理过程以及返回数据的意义都至关重要。
图 1 – WebAuthn 注册流程及各步骤中的相关重要数据首先,即图中步骤 0 所示,应用程序发起注册请求,该请求的协议与格式均不在 Web Authentication API 的范围内。
后面的注册步骤如下:
完整的验证步骤可查看 Web Authentication API 规范。检查完成后,服务器会将与用户账户相关联的新公钥进行存储,以供将来用户希望使用公钥进行身份验证时使用。
用户在 WebAuthn 注册完成之后就可以进行身份验证(或者说登录)。验证的流程与注册流程相似,图 2 所示也与图 1 相类似。但是,二者的主要区别在于:1)验证不需要用户或依赖方信息;2)验证使用之前生成的密钥对来创建断言,而不是用认证器生产时就刻录进去的密钥对来创建证明。和上文一样,以下是对验证流程的简要概述,并不会详细描述 WebAuthn API 的每个环节或细节。验证所需的数据可查看 PublicKeyCredentialRequestOptions 字典,返回的数据可查看 PublicKeyCredential 接口(其中 PublicKeyCredential.response 是 AuthenticatorAssertionResponse 接口)。
图 2 –WebAuthn 验证流程及各步骤中的相关重要数据后面的验证步骤如下:
验证断言的完整步骤可查看 Web Authentication API 规范。验证完成后,服务器会注意到用户身份已被验证。此步骤超出了 Web Authentication API 规范的范围,但有一个选项是给用户会话删除新 cookie。
Credential 提供关于实体的信息,作为信任决策的先决条件。
CredentialsContainer 公开请求凭证的方法,并在成功登录或退出时通知用户代理。该接口可通过 Navigator.credentials 访问。Web Authentication 规范在 create() 和 get() 方法中添加了 publicKey 成员,分别是为了创建新的公钥对与获取密钥对验证。
PublicKeyCredential 该接口提供关于公钥/私钥对的信息,是一个使用防网络钓鱼与数据泄露的非对称密钥对进行服务登录的凭证,能够有效取代密码。
AuthenticatorResponse AuthenticatorAttestationResponse 和 AuthenticatorAssertionResponse 的基接口,二者为密钥对提供加密信任根。二者分别由 CredentialsContainer.create() 和 CredentialsContainer.get() 返回,这些子接口包含来自浏览器的信息,如挑战、origin。二者都可以通过 PublicKeyCredential.response 返回。
AuthenticatorAttestationResponse PublicKeyCredential 传递时,由 CredentialsContainer.create() 返回,该接口还能为已生成的新密钥对提供加密信任根。
AuthenticatorAssertionResponse PublicKeyCredential 传递时,由 CredentialsContainer.get() 返回,该接口还能向服务证明拥有密钥对且验证请求有效、已批准同意。
PublicKeyCredentialCreationOptions 传递到 CredentialsContainer.create() 的选项。
PublicKeyCredentialRequestOptions 传递到 CredentialsContainer.get() 的选项。
Demo 网站
注意,出于安全考虑,如果浏览器窗口在调用过程中失去焦点,web authentication 调用 create() 和 get() 会被取消。
// sample arguments for registration
var createCredentialDefaultArgs = {
publicKey: {
// Relying Party (a.k.a. - Service):
rp: {
name: "Acme"
},
// User:
user: {
id: new Uint8Array(16),
name: "john.p.smith@example.com",
displayName: "John P. Smith"
},
pubKeyCredParams: [{
type: "public-key",
alg: -7
}],
attestation: "direct",
timeout: 60000,
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server
0x8C, 0x0A, 0x26, 0xFF, 0x22, 0x91, 0xC1, 0xE9, 0xB9, 0x4E, 0x2E, 0x17, 0x1A, 0x98, 0x6A, 0x73,
0x71, 0x9D, 0x43, 0x48, 0xD5, 0xA7, 0x6A, 0x15, 0x7E, 0x38, 0x94, 0x52, 0x77, 0x97, 0x0F, 0xEF
]).buffer
}
};
// sample arguments for login
var getCredentialDefaultArgs = {
publicKey: {
timeout: 60000,
// allowCredentials: [newCredential] // see below
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server
0x79, 0x50, 0x68, 0x71, 0xDA, 0xEE, 0xEE, 0xB9, 0x94, 0xC3, 0xC2, 0x15, 0x67, 0x65, 0x26, 0x22,
0xE3, 0xF3, 0xAB, 0x3B, 0x78, 0x2E, 0xD5, 0x6F, 0x81, 0x26, 0xE2, 0xA6, 0x01, 0x7D, 0x74, 0x50
]).buffer
},
};
// register / create a new credential
navigator.credentials.create(createCredentialDefaultArgs)
.then((cred) => {
console.log("NEW CREDENTIAL", cred);
// normally the credential IDs available for an account would come from a server
// but we can just copy them from above...
var idList = [{
id: cred.rawId,
transports: ["usb", "nfc", "ble"],
type: "public-key"
}];
getCredentialDefaultArgs.publicKey.allowCredentials = idList;
return navigator.credentials.get(getCredentialDefaultArgs);
})
.then((assertion) => {
console.log("ASSERTION", assertion);
})
.catch((err) => {
console.log("ERROR", err);
});
Web Authentication: An API for accessing Public Key Credentials
文章来源:https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API