You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebAuthn代码在Mac端运行正常但iOS Safari端失效问题咨询

失效原因

  • iOS Safari强制要求navigator.credentials.create和navigator.credentials.get必须由*用户主动交互(点击、触摸等)*触发,你当前代码是页面加载后自动执行的匿名函数,会被Safari直接拦截,这是最核心的失效原因。
  • pubKeyCredParams对象中非法加入了transports字段,该字段是allowCredentials数组条目的专属属性,Mac端浏览器对该错误做了兼容忽略,iOS Safari会严格校验参数合法性直接拒绝执行。
  • 页面存在重复IDclientDataJSON,DOM查询时会返回非预期元素,导致内容渲染异常。
  • 原printHex函数存在逗号运算符误用问题,字节长度信息无法正常拼接,虽不影响功能执行但会导致输出异常。

修复后可兼容iOS Safari的代码

<script>
    function printHex(msg, buf) {
        if (ArrayBuffer.isView(buf) && buf.buffer instanceof ArrayBuffer) {
            buf = buf.buffer;
        }
        var response = '';

        if ((typeof msg != "string") ||
            (typeof buf != "object")) {
            return "Bad args to printHex";
        }
        if (!(buf instanceof ArrayBuffer)) {
            return "Attempted printHex with non-ArrayBuffer:" + buf;
        }

        var arr = new Uint8Array(buf);
        var len = buf.byteLength;
        var i;
        var str = "";
        response += msg + `(${buf.byteLength} bytes)`;
        for (i = 0; i < len; i++) {
            var hexch = arr[i].toString(16);
            hexch = (hexch.length == 1) ? ("0" + hexch) : hexch;
            str += hexch.toUpperCase() + " ";
            if (i && !((i + 1) % 16)) {
                response += str;
                str = "";
            }
        }

        if ((i) % 16) {
            response += str;
        }
        return response;
    }

    var createCredentialDefaultArgs = {
        publicKey: {
            rp: {
                name: "Acme",
                icon: "https://www.w3.org/StyleSheets/TR/2016/logos/W3C"
            },
            user: {
                id: new Uint8Array(16),
                name: "test@test.com",
                displayName: "test"
            },
            pubKeyCredParams: [{
                type: "public-key",
                alg: -7
            }],
            attestation: "direct",
            timeout: 60000,
            challenge: new Uint8Array([
                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,
                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
        }
    };

    var getCredentialDefaultArgs = {
        publicKey: {
            timeout: 60000,
            challenge: new Uint8Array([
                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,
                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
        },
    };

    // 绑定到按钮点击事件,满足iOS Safari的用户交互触发要求
    document.getElementById('startAuth').addEventListener('click', async () => {
        try {
            var cred = await navigator.credentials.create(createCredentialDefaultArgs);
            console.log("NEW CREDENTIAL", cred);
            document.getElementById("rawId").innerHTML = printHex("rawId: ", cred.rawId);

            document.getElementById("attestationObject").innerHTML = printHex("attestationObject: ", cred.response.attestationObject);
            document.getElementById("regClientDataJSON").innerHTML = printHex("注册阶段clientDataJSON: ", cred.response.clientDataJSON);

            var idList = [{
                id: cred.rawId,
                transports: ["internal"],
                type: "public-key"
            }];
            getCredentialDefaultArgs.publicKey.allowCredentials = idList;
            var assertion = await navigator.credentials.get(getCredentialDefaultArgs);
            console.log("ASSERTION", assertion);
            document.getElementById("assertion").innerHTML = assertion;
            document.getElementById("authClientDataJSON").innerHTML = printHex("认证阶段clientDataJSON: ", assertion.response.clientDataJSON);
            document.getElementById("authenticatorData").innerHTML = printHex("authenticatorData: ", assertion.response.authenticatorData);
            document.getElementById("signature").innerHTML = printHex("signature: ", assertion.response.signature);
            document.getElementById("userHandle").innerHTML = printHex("userHandle: ", assertion.response.userHandle);
            console.log("done")
        } catch (err) {
            console.log("ERROR:", err)
        }
    })

</script>

<button id="startAuth">启动WebAuthn认证</button>
<div id="rawId"></div>
<div id="attestationObject"></div>
<div id="regClientDataJSON"></div>

<div id="assertion"></div>
<div id="authClientDataJSON"></div>
<div id="authenticatorData"></div>
<div id="signature"></div>
<div id="userHandle"></div>

内容的提问来源于stack exchange,提问作者Ahmad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 04:06:03