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
相关产品推荐
相关产品推荐

