From bd6b518aae61608ddc2d82b43ccc283dc95b9c54 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期三, 11 三月 2026 13:59:33 +0800
Subject: [PATCH] #
---
src/main/webapp/static/js/webauthn-utils.js | 141 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 141 insertions(+), 0 deletions(-)
diff --git a/src/main/webapp/static/js/webauthn-utils.js b/src/main/webapp/static/js/webauthn-utils.js
new file mode 100644
index 0000000..a54eb60
--- /dev/null
+++ b/src/main/webapp/static/js/webauthn-utils.js
@@ -0,0 +1,141 @@
+(function (window) {
+ "use strict";
+
+ function base64UrlToArrayBuffer(base64Url) {
+ var value = String(base64Url || "").replace(/-/g, "+").replace(/_/g, "/");
+ var padding = value.length % 4;
+ if (padding) {
+ value += new Array(5 - padding).join("=");
+ }
+ var binary = window.atob(value);
+ var bytes = new Uint8Array(binary.length);
+ for (var i = 0; i < binary.length; i++) {
+ bytes[i] = binary.charCodeAt(i);
+ }
+ return bytes.buffer;
+ }
+
+ function arrayBufferToBase64Url(buffer) {
+ var bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer || []);
+ var binary = "";
+ for (var i = 0; i < bytes.length; i++) {
+ binary += String.fromCharCode(bytes[i]);
+ }
+ return window.btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
+ }
+
+ function normalizeArray(value) {
+ return Array.isArray(value) ? value : [];
+ }
+
+ function toCreationOptions(payload) {
+ var publicKey = {
+ challenge: base64UrlToArrayBuffer(payload.challenge),
+ rp: {
+ id: payload.rpId,
+ name: payload.rpName || "WCS"
+ },
+ user: {
+ id: base64UrlToArrayBuffer(payload.userId),
+ name: payload.userName,
+ displayName: payload.userDisplayName || payload.userName
+ },
+ pubKeyCredParams: normalizeArray(payload.pubKeyCredParams),
+ timeout: Number(payload.timeout || 60000),
+ attestation: payload.attestation || "none",
+ authenticatorSelection: payload.authenticatorSelection || {
+ residentKey: "preferred",
+ userVerification: "required"
+ }
+ };
+ var excludeCredentials = normalizeArray(payload.excludeCredentials).map(function (item) {
+ return {
+ type: item.type || "public-key",
+ id: base64UrlToArrayBuffer(item.id),
+ transports: normalizeArray(item.transports)
+ };
+ });
+ if (excludeCredentials.length) {
+ publicKey.excludeCredentials = excludeCredentials;
+ }
+ return { publicKey: publicKey };
+ }
+
+ function toRequestOptions(payload) {
+ var publicKey = {
+ challenge: base64UrlToArrayBuffer(payload.challenge),
+ rpId: payload.rpId,
+ timeout: Number(payload.timeout || 60000),
+ userVerification: payload.userVerification || "required"
+ };
+ var allowCredentials = normalizeArray(payload.allowCredentials).map(function (item) {
+ return {
+ type: item.type || "public-key",
+ id: base64UrlToArrayBuffer(item.id),
+ transports: normalizeArray(item.transports)
+ };
+ });
+ if (allowCredentials.length) {
+ publicKey.allowCredentials = allowCredentials;
+ }
+ return { publicKey: publicKey };
+ }
+
+ function ensureSupported() {
+ if (!window.isSecureContext) {
+ throw new Error("secure-context");
+ }
+ if (!window.PublicKeyCredential || !window.navigator || !window.navigator.credentials) {
+ throw new Error("not-supported");
+ }
+ }
+
+ async function register(payload) {
+ ensureSupported();
+ var credential = await window.navigator.credentials.create(toCreationOptions(payload));
+ if (!credential || !credential.response) {
+ throw new Error("create-empty");
+ }
+ var response = credential.response;
+ if (typeof response.getPublicKey !== "function" || typeof response.getAuthenticatorData !== "function") {
+ throw new Error("extension-unsupported");
+ }
+ var publicKey = response.getPublicKey();
+ var authenticatorData = response.getAuthenticatorData();
+ if (!publicKey || !authenticatorData) {
+ throw new Error("public-key-missing");
+ }
+ return {
+ credentialId: arrayBufferToBase64Url(credential.rawId),
+ clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
+ authenticatorData: arrayBufferToBase64Url(authenticatorData),
+ publicKey: arrayBufferToBase64Url(publicKey),
+ publicKeyAlgorithm: response.getPublicKeyAlgorithm(),
+ transports: JSON.stringify(typeof response.getTransports === "function" ? response.getTransports() || [] : [])
+ };
+ }
+
+ async function authenticate(payload) {
+ ensureSupported();
+ var credential = await window.navigator.credentials.get(toRequestOptions(payload));
+ if (!credential || !credential.response) {
+ throw new Error("get-empty");
+ }
+ var response = credential.response;
+ return {
+ credentialId: arrayBufferToBase64Url(credential.rawId),
+ clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
+ authenticatorData: arrayBufferToBase64Url(response.authenticatorData),
+ signature: arrayBufferToBase64Url(response.signature),
+ userHandle: response.userHandle ? arrayBufferToBase64Url(response.userHandle) : ""
+ };
+ }
+
+ window.WCS_WEBAUTHN = {
+ isSupported: function () {
+ return !!(window.isSecureContext && window.PublicKeyCredential && window.navigator && window.navigator.credentials);
+ },
+ register: register,
+ authenticate: authenticate
+ };
+})(window);
--
Gitblit v1.9.1