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

Keycloak 22:自定义前端通过API实现WebAuthn Passkey认证

自定义前端对接Keycloak 22 WebAuthn Passkey方案

Keycloak 22支持通过API对接自定义前端完成WebAuthn Passkey的注册与认证,无需依赖自带前端,核心是利用其WebAuthn相关后台接口结合浏览器原生WebAuthn API实现。以下分注册、认证两个流程说明:

一、Passkey注册流程(已完成用户名密码登录后)

注册需要用户先通过常规方式登录,拿到有效access_token(用于身份校验):

  • 步骤1:获取WebAuthn注册选项
    发送POST请求到Keycloak用户凭证接口,传入用户ID和验证偏好:

    POST {keycloak-domain}/realms/{your-realm}/users/{user-id}/credentials/webauthn/register-options
    Authorization: Bearer {access_token}
    Content-Type: application/json
    
    {
      "userVerification": "preferred",
      "attestation": "none"
    }
    

    接口会返回WebAuthn标准的publicKey配置对象(即CredentialCreationOptions)。

  • 步骤2:前端生成Passkey凭证
    用浏览器原生API调用Passkey创建:

    const publicKey = // 接口返回的publicKey对象
    const credential = await navigator.credentials.create({ publicKey });
    
    // 将凭证数据转为URL安全的Base64编码
    const attestationObject = btoa(String.fromCharCode(...new Uint8Array(credential.response.attestationObject)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    const clientDataJSON = btoa(String.fromCharCode(...new Uint8Array(credential.response.clientDataJSON)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    
  • 步骤3:提交凭证到Keycloak完成注册
    发送POST请求保存凭证:

    POST {keycloak-domain}/realms/{your-realm}/users/{user-id}/credentials/webauthn
    Authorization: Bearer {access_token}
    Content-Type: application/json
    
    {
      "attestationObject": "{base64url编码的attestationObject}",
      "clientDataJSON": "{base64url编码的clientDataJSON}",
      "userVerification": "preferred",
      "name": "My Laptop Passkey" // 自定义凭证名称,方便用户识别
    }
    

    响应状态码201则注册成功。

二、Passkey认证流程(替代用户名密码登录)

直接用Passkey完成身份校验并获取token:

  • 步骤1:获取WebAuthn认证挑战
    发送POST请求获取认证所需的publicKey配置(CredentialRequestOptions):

    POST {keycloak-domain}/realms/{your-realm}/webauthn/authentication-options
    Content-Type: application/json
    
    {
      "username": "{user-username/email}",
      "userVerification": "preferred"
    }
    
  • 步骤2:前端获取Passkey凭证
    调用浏览器API获取认证凭证:

    const publicKey = // 接口返回的publicKey对象
    const credential = await navigator.credentials.get({ publicKey });
    
    // 转换为URL安全Base64编码
    const authenticatorData = btoa(String.fromCharCode(...new Uint8Array(credential.response.authenticatorData)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    const clientDataJSON = btoa(String.fromCharCode(...new Uint8Array(credential.response.clientDataJSON)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    const signature = btoa(String.fromCharCode(...new Uint8Array(credential.response.signature)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    const userHandle = credential.response.userHandle 
      ? btoa(String.fromCharCode(...new Uint8Array(credential.response.userHandle)))
          .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
      : null;
    
  • 步骤3:提交凭证获取OAuth2 Token
    用自定义grant_type请求token:

    POST {keycloak-domain}/realms/{your-realm}/protocol/openid-connect/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=urn:ietf:params:oauth:grant-type:webauthn
    &client_id={your-client-id}
    &username={user-username/email}
    &authenticatorData={base64url编码的authenticatorData}
    &clientDataJSON={base64url编码的clientDataJSON}
    &signature={base64url编码的signature}
    &userHandle={base64url编码的userHandle(可选)}
    

    成功后会返回标准的access_token、id_token和refresh_token,完成登录。

关键注意事项

  • 所有二进制数据必须转换为URL安全的Base64编码(替换+为-,/为_,去掉末尾的=)
  • 需确保Keycloak Realm已启用WebAuthn认证器:在Authentication > Flows中,将WebAuthn添加到认证流程(或作为可选认证方式)
  • 注册阶段必须先完成用户身份认证(如用户名密码登录),确保access_token具备修改用户凭证的权限
  • 若客户端是公共客户端(如SPA),需在Keycloak客户端设置中开启Direct Access Grants Enabled(或确保对应grant_type被允许)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 06:22:49