本文介绍 WebAuthn 的接入。
前提条件
请完成获取阿里云访问密钥;
请确保使用的应用,已经勾选了“WebAuthn”的认证方式,请查看修改应用认证方式。
WebAuthn认证器注册-初始化
webAuthn认证器注册-初始化请求,返回符合webAuthn创建身份证明的参数对象。
请求参数
名称 | 类型 | 必填 | 示例值 | 描述 |
ApplicationExternalId | String | 是 | A0000001 | 应用外部ID,该字段在创建应用时指定,在应用详情页中查看 |
AuthenticatorType | String | 是 | WEBAUTHN | 认证方式标识,调用WEBAUTHN认证接口时,固定值为:WEBAUTHN |
UserId | String | 是 | user-test-1 | 用户的唯一标识, 例如:userId、手机号、邮箱等 |
Username | String | 否 | 张三 | 用户名 |
UserDisplayName | String | 否 | 小张 | 用户昵称 |
ServerExtendParamsJson | String | 否 | eyJhdHRhY2htZW50IjoicGxhdGZvcm0iLCJ1c2VyVmVyaWZpY2F0aW9uIjoicmVxdWlyZWQifQ== | webAuthn注册上下文, JSON字符串 |
ServerExtendParamsJson参数解析
名称 | 类型 | 必填 | 示例值 | 描述 |
attachment | String | 否 | platform | 默认 |
userVerification | String | 否 | required | 默认 |
返回数据
名称 | 类型 | 示例值 | 描述 |
Success | boolean | true | 操作结果,true 代表成功,false 代表失败 |
Code | String | Opreation.Success | 状态码。当 Success=true 时,Code=Operation.Success当 Success=false 时,Code的值,请参见下方错误码 |
Message | String | Opreation.Success | 具体的描述信息,当 Success=false 时,会给出Code的对应的具体描述信息 |
RequestId | String | 1C0EE50A-B3BB-42FD-AB59-E3FE88976982 | 请求ID |
Data | String | {"challengeBase64":"AAAAAXzBzvy0MTIzMjMxMjMxMg==","requestId":"2D503C0A-57FB-556E-B7BD-5E117EFA1541","options":"{\"attestation\":\"none\",\"authenticatorSelection\":{\"userVerification\":\"required\"},\"challengeBase64\":\"AAAAAXzBzvy0MTIzMjMxMjMxMg==\",\"excludeCredentials\":[],\"pubKeyCredParams\":[{\"alg\":-7,\"type\":\"public-key\"},{\"alg\":-257,\"type\":\"public-key\"}],\"rp\":{\"id\":\"localhost\",\"name\":\"测试(\"},\"timeout\":60000,\"user\":{\"displayName\":\"1232312312\",\"id\":\"1232312312\",\"name\":\"1232312312\"}}"} | 操作成功时,Data中的JSON字符串包含两个字段:
操作失败时,Data字段为空 |
请求示例
请求方式:POST
https://idaas-doraemon.aliyuncs.com/?Action=CreateAuthenticatorRegistration
&Version=2021-05-20
&ApplicationExternalId=A0000001
&AuthenticatorType=WEBAUTHN
&UserId=user-test-1
&UserName=user-test-1
&UserDisplayName=user-test-1
&ServerExtendParamsJson=eyJhcHBJ...biI6IjEuMCJ9
&<公共请求参数>
正常返回示例 -WebAuthn注册认证器-初始化通过
{
"Success": true,
"Code": "Operation.Success",
"Message": "Operation.Success",
"RequestId": "337848D2-FF8A-4EDD-BD4D-1B9BC80E58B6",
"Data": {"challengeBase64":"AAAAAXzAn9aPMTIzMjMxMjMxMg==",
"requestId":"91DBDD36-B05C-5AF1-B205-E1708EC5B792",
"options":"{\"attestation\":\"none\",\"authenticatorSelection\":{\"userVerification\":\"required\"},\"challengeBase64\":\"AAAAAXzAn9aPMTIzMjMxMjMxMg==\",\"excludeCredentials\":[],\"pubKeyCredParams\":[{\"alg\":-7,\"type\":\"public-key\"},{\"alg\":-257,\"type\":\"public-key\"}],\"rp\":{\"id\":\"localhost\",\"name\":\"测试(勿删)\"},\"timeout\":60000,\"user\":{\"displayName\":\"1232312312\",\"id\":\"1232312312\",\"name\":\"1232312312\"}}"
}
}
错误返回示例 - 请求参数中缺少认证方式标识
{
"Success": false,
"Code": "Params.Blank",
"Message": "Params.Blank.APIInvokeParams.AuthenticatorType",
"RequestId": "1C0EE50A-B3BB-42FD-AB59-E3FE88976982",
"Data":null
}
WebAuthn认证器注册-验证
webAuthn认证器注册验证,用于注册webAuthn认证器,注册认证器主要工作是校验合法性、对用户ID和认证器绑定起来。
适用场景:注册场景、开启webAuthn认证用做免密登录、二次认证
请求参数
名称 | 类型 | 必填 | 示例值 | 描述 |
ApplicationExternalId | String | 是 | A0000001 | 应用外部ID,该字段在创建应用时指定,在应用详情页中查看 |
AuthenticatorType | String | 是 | WEBAUTHN | 认证方式标识,调用WEBAUTHN认证接口时,固定值为:WEBAUTHN |
UserId | String | 是 | user-test-1 | 用户的唯一标识, 例如:userId、手机号、邮箱等 |
AuthenticatorName | String | 否 | webAuthn认证器 | 认证器名称 |
RequireChallengeBase64 | String | 否 | AAAAAXzAn9aPMTIzMjMxMjMxMg== | 创建认证器初始化请求返回的ChallengeBase64 |
ServerExtendParamsJson | String | 否 | eyJhdHRhY2htZW50IjoicGxhdGZvcm0iLCJ1c2VyVmVyaWZpY2F0aW9uIjoicmVxdWlyZWQifQ== | webAuthn注册上下文, JSON字符串 |
RegistrationContext | String | 是 | JS SDK生成的注册信息,JSON字符串,具体包含参数见下方RegistrationContext参数解析 | |
UserSourceIp | String | 否 | 127.0.0.1 | 客户端IP |
RegistrationContext参数解析:
如果是webauthn
,则其中RegistrationContext
是一个JSON,包含以下信息:
字段 | 类型 | 备注 |
credentialId | String | 身份证明ID,即 URI-ENCODED-BASE64(RAW_ID) 以此信息唯一标识一个webauthn认证器 |
type | String | 当前固定是 |
transports | String | 办理方式,由 WebAuthn 接口提供 |
attestationObjectBase64 | String | 注册认证对象 |
clientDataJSONBase64 | String | 客户端数据JSON |
ServerExtendParamsJson参数解析:
当WebAuthn时,ServerExtendParamsJson可以为空(即默认值),或者配置以下参数:
字段 | 类型 | 是否必选 | 备注 |
requireUserPresence | Boolean | 否 | 默认值为 |
requireUserVerification | Boolean | 否 | 默认值为 |
返回数
名称 | 类型 | 示例值 | 描述 |
Success | boolean | true | 操作结果,true 代表成功,false 代表失败 |
Code | String | Opreation.Success | 状态码。当 Success=true 时,Code=Operation.Success当 Success=false 时,Code的值,请参见下方错误码 |
Message | String | Opreation.Success | 具体的描述信息,当 Success=false 时,会给出Code的对应的具体描述信息 |
RequestId | String | 1C0EE50A-B3BB-42FD-AB59-E3FE88976982 | 请求ID |
Data | String | {"authenticatorUuid": "1C0EE50AB3BB42FDAB59E3FE88976982"} | 操作成功时,Data中的JSON字符串包含1个字段:
操作失败时,Data字段为空 |
请求示例
请求方式:POST
https://idaas-doraemon.aliyuncs.com/?Action=RegisterAuthenticator
&Version=2021-05-20
&ApplicationExternalId=A0000001
&AuthenticatorType=WEBAUTHN
&UserId=user-test-1
&AuthenticatorName=user-test-1
&RequireChallengeBase64="MGVhMGViYzYzNjc4YWZlYzMxYmIzODUxYTlhNzViMjFHdU5kMjIySjZtYw=="
&ServerExtendParamsJson=eyJhcHBJ...biI6IjEuMCJ9
&RegistrationContext="xxxx"
&UserSourceIp=1.1.1.1
&<公共请求参数>
正常返回示例 -WebAuthn注册认证器-验证通过
{
"Success": true,
"Code": "Operation.Success",
"Message": "Operation.Success",
"RequestId": "337848D2-FF8A-4EDD-BD4D-1B9BC80E58B6",
"Data": {
"authenticatorUuid": "1C0EE50AB3BB42FDAB59E3FE88976982"
}
}
错误返回示例 - 请求参数中缺少认证方式标识
{
"Success": false,
"Code": "Params.Blank",
"Message": "Params.Blank.APIInvokeParams.AuthenticatorType",
"RequestId": "1C0EE50A-B3BB-42FD-AB59-E3FE88976982",
"Data":null
}
WebAuthn认证器认证-初始化
webAuthn认证器认证-初始化请求。
请求参数
名称 | 类型 | 必填 | 示例值 | 描述 |
ApplicationExternalId | String | 是 | A0000001 | 应用外部ID,该字段在创建应用时指定,在应用详情页中查看 |
AuthenticatorType | String | 是 | WEBAUTHN | 认证方式标识,调用WEBAUTHN认证接口时,固定值为:WEBAUTHN |
UserId | String | 是 | user-test-1 | 用户的唯一标识, 例如:userId、手机号、邮箱等 |
BindHashBase64 | String | 否 | 用于生成Challenge与参数BindHash绑定,在具体业务中可以和用户的一笔交易Hash绑定,也就说一次WebAuthn的认证可以和用户具体的一笔业务发生绑定关系 |
返回数据
名称 | 类型 | 示例值 | 描述 |
Success | boolean | true | 操作结果,true 代表成功,false 代表失败 |
Code | String | Opreation.Success | 状态码。当 Success=true 时,Code=Operation.Success当 Success=false 时,Code的值,请参见下方错误码 |
Message | String | Opreation.Success | 具体的描述信息,当 Success=false 时,会给出Code的对应的具体描述信息 |
RequestId | String | 1C0EE50A-B3BB-42FD-AB59-E3FE88976982 | 请求ID |
Data | String | "challengeBase64": "AQAAAXydKjV+NlhrSlpPMHAxeHZPWlFJQTBiNXlRWU5s", "options": "{\"allowCredentials\":[{\"idBase64\":\"Z0MkHYJnRDbGfa0LvWvvO6UqcNjFwCIR6mpNTl8jfTA\",\"transports\":[],\"type\":\"public-key\"}],\"challengeBase64\":\"AQAAAXydKjV+NlhrSlpPMHAxeHZPWlFJQTBiNXlRWU5s\",\"rpId\":\"localhost\",\"timeout\":60000,\"userVerification\":\"preferred\"}" } | 操作成功时,Data中的JSON字符串包含两个字段:
操作失败时,Data字段为空 |
请求示例
请求方式:POST
https://idaas-doraemon.aliyuncs.com/?Action=CreateUserAuthenticateOptions
&Version=2021-05-20
&ApplicationExternalId=A0000001
&AuthenticatorType=WEBAUTHN
&UserId=user-test-1
&BindHashBase64=xxxxx
&<公共请求参数>
正常返回示例 -WebAuthn认证器认证-初始化通过
{
"Success": true,
"Code": "Operation.Success",
"Message": "Operation.Success",
"RequestId": "337848D2-FF8A-4EDD-BD4D-1B9BC80E58B6",
"Data": {
"challengeBase64": "AQAAAXydKjV+NlhrSlpPMHAxeHZPWlFJQTBiNXlRWU5s",
"options": "{\"allowCredentials\":[{\"idBase64\":\"Z0MkHYJnRDbGfa0LvWvvO6UqcNjFwCIR6mpNTl8jfTA\",\"transports\":[],\"type\":\"public-key\"}],\"challengeBase64\":\"AQAAAXydKjV+NlhrSlpPMHAxeHZPWlFJQTBiNXlRWU5s\",\"rpId\":\"localhost\",\"timeout\":60000,\"userVerification\":\"preferred\"}"
}
}
错误返回示例 - 请求参数中缺少认证方式标识
{
"Success": false,
"Code": "Params.Blank",
"Message": "Params.Blank.APIInvokeParams.AuthenticatorType",
"RequestId": "1C0EE50A-B3BB-42FD-AB59-E3FE88976982",
"Data":null
}
WebAuthn认证器认证
webAuthn认证器认证。
名称 | 类型 | 必填 | 示例值 | 描述 |
ApplicationExternalId | String | 是 | A0000001 | 应用外部ID,该字段在创建应用时指定,在应用详情页中查看 |
AuthenticatorType | String | 是 | WEBAUTHN | 认证器类型,固定值为: WEBAUTHN |
AuthenticationContext | String | 是 | JS SDK生成的信息 | |
UserId | String | 是 | user-test-1 | 用户ID |
ServerExtendParamsJson | String | 否 | 用于配置UP、UV及SignCount递增 | |
RequireChallengeBase64 | String | 否 | 默认无则不校验,只校验Challenge时效及重放 | |
RequireBindHashBase64 | String | 否 | 作用参看"WebAuthn认证器认证-初始化"里的参数"BindHashBase64" | |
UserSourceIp | String | 否 | 127.0.0.1 | 用户IP,用于记录到审计日志中 |
AuthenticationContext参数解析:
如果是webauthn
,则AuthenticationContext
是一个JSON,包含以下信息:
字段 | 类型 | 备注 |
userAgent | String | 应用代理 |
credentialId | String | 身份证明ID |
type | String | 当前固定是 |
authenticatorDataBase64 | String | 认证数据JSON |
clientDataJSONBase64 | String | 客户端数据JSON |
signatureBase64 | String | 签名 |
userHandleBase64 | String | 用户Handle(也就是UserId) |
ServerExtendParamsJson参数解析:
当WebAuthn时,ServerExtendParamsJson可以为空(即默认值),或者配置以下参数:
字段 | 类型 | 是否必选 | 备注 |
requireUserPresence | Boolean | 否 | 默认值与认证器的UP值一致,是否要求用户出席 |
requireUserVerification | Boolean | 否 | 默认值与认证器的UV值一致,是否要求用户验证 |
requireSignCountIncrement | Boolean | 否 | 默认值为 |
名称 | 类型 | 示例值 | 描述 |
Success | boolean | true | 操作结果,true 代表成功,false 代表失败 |
Code | String | Opreation.Success | 状态码。
|
Message | String | Opreation.Success | 具体的描述信息,当 Success=false 时,会给出Code的对应的具体描述信息 |
RequestId | String | 1C0EE50A-B3BB-42FD-AB59-E3FE88976982 | 请求ID |
Data | String | { "verifyResult": true, "authenticateResultInfo": { "credentialId": "Z0MkHYJnRDbGfa0LvWvvO6UqcNjFwCIR6mpNTl8jfTA", "bindHashBase64": null, "userId": "abcdefg" } | 操作成功时,Data中的JSON字符串包含5个字段:
操作失败时,Data字段为空 |
请求方式:POST
https://idaas-doraemon.aliyuncs.com/?Action=VerifyUserAuthentication
&Version=2021-05-20
&ApplicationExternalId=A0000001
&AuthenticatorType=WEBAUTHN
&UserId=user-test-1
&AuthenticationContext=xxxxx
&ServerExtendParamsJson=eyJwaG9uZU51bWJlciI6IjAwMDAwMDAwMDAwIn0=
&UserSourceIp=1.1.1.1
&<公共请求参数>
正常返回示例 -创建webAuthn认证器认证请求通过
{
"Success": true,
"Code": "Operation.Success",
"Message": "Operation.Success",
"RequestId": "337848D2-FF8A-4EDD-BD4D-1B9BC80E58B6",
"Data": {
"verifyResult": true,
"authenticateResultInfo": {
"credentialId": "Z0MkHYJnRDbGfa0LvWvvO6UqcNjFwCIR6mpNTl8jfTA",
"bindHashBase64": null,
"userId": "abcdefg"
}
}
}
错误返回示例 - 请求参数中缺少认证类型标识
{
"Success": false,
"Code": "Params.Blank",
"Message": "Params.Blank.APIInvokeParams.AuthenticatorType",
"RequestId": "1C0EE50A-B3BB-42FD-AB59-E3FE88976982",
"Data":null
}
代码示例(Java)
webAuthn认证的示例代码:
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.idaas_doraemon.model.v20210520.*;
import com.aliyuncs.profile.DefaultProfile;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class IDaaSAuthSample {
/**
* 使用AK&SK初始化账号Client
*
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
public static IAcsClient createClient(String accessKeyId, String accessKeySecret) throws Exception {
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
// 此处以把AccessKey 和 AccessKeySecret 保存在环境变量为例说明。您可以根据业务需要,保存到配置文件里。
// 强烈建议不要把 AccessKey 和 AccessKeySecret 保存到代码里,会存在密钥泄漏风险
String accessKeyId = System.getenv("ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ACCESS_KEY_SECRET");
DefaultProfile profile = DefaultProfile.getProfile(
"cn-hangzhou",
accessKeyId,
accessKeySecret);
// addEndpoint
DefaultProfile.addEndpoint("cn-hangzhou",
"idaas-doraemon",
"idaas-doraemon.aliyuncs.com");
// 访问的域名
return new DefaultAcsClient(profile);
}
public static void main(String[] args_) throws Exception {
// webAuthn认证器注册初始化
registerWebAuthnInit();
// webAuthn认证器注册
registerWebAuthnVerify();
// webAuthn认证初始化
webAuthnVerifyInit();
// webAuthnVerify认证
}
public void registerWebAuthnInit() throws Exception {
IAcsClient client = IDaaSAuthSample.createClient("Your AccessKey", "Your AccessSecret");
//webAuthn认证器注册初始化
CreateAuthenticatorRegistrationRequest request = new CreateAuthenticatorRegistrationRequest();
request.setApplicationExternalId("testApplication");
request.setAuthenticatorType("WEBAUTHN");
request.setUserId("1232312312");
request.setUserName("1232312312");
request.setUserDisplayName("1232312312");
request.setServerExtendParamsJson("eyJhdHRhY2htZW50IjoicGxhdGZvcm0iLCJ1c2VyVmVyaWZpY2F0aW9uIjoicmVxdWlyZWQifQ==");
// 复制代码运行请自行打印 API 的返回值
try {
CreateAuthenticatorRegistrationResponse response = client.getAcsResponse(request);
System.out.println(response.getChallengeBase64());
} catch (Exception e) {
//根据e,getCode()判断异常原因
System.out.println(e);
}
}
public void registerWebAuthnVerify() throws Exception {
IAcsClient client = IDaaSAuthSample.createClient("Your AccessKey", "Your AccessSecret");
//webAuthn认证器注册-验证
RegisterAuthenticatorRequest request = new RegisterAuthenticatorRequest();
request.setApplicationExternalId("testApplication");
request.setAuthenticatorType("WEBAUTHN");
request.setUserId("1232312312");
request.setAuthenticatorName("认证器");
request.setRequireChallengeBase64("AAAAAXy71/wtMTIzMjMxMjMxMg==");
request.setRegistrationContext("{\"base64Challenge\":\"AAAAAXy71/wtMTIzMjMxMjMxMg==\",\"otpCode\":\"304288\"}");
request.setUserSourceIp("47.100.XX.XX");
// 复制代码运行请自行打印 API 的返回值
try {
RegisterAuthenticatorResponse response = client.getAcsResponse(request);
System.out.println(response.getAuthenticatorUuid());
} catch (Exception e) {
//根据e,getCode()判断异常原因
System.out.println(e);
}
}
public void webAuthnVerifyInit() throws Exception {
IAcsClient client = IDaaSAuthSample.createClient("Your AccessKey", "Your AccessSecret");
//WebAuthn认证器-验证初始化
CreateUserAuthenticateOptionsRequest request = new CreateUserAuthenticateOptionsRequest();
request.setApplicationExternalId("testApplication");
request.setAuthenticatorType("WEBAUTHN");
request.setUserId("1232312312");
// 复制代码运行请自行打印 API 的返回值
try {
CreateUserAuthenticateOptionsResponse response = client.getAcsResponse(request);
System.out.println(response.getOptions());
} catch (Exception e) {
//根据e,getCode()判断异常原因
System.out.println(e);
}
}
public void webAuthnVerify() throws Exception {
IAcsClient client = IDaaSAuthSample.createClient("Your AccessKey", "Your AccessSecret");
//WebAuthn认证器-验证
VerifyUserAuthenticationRequest request = new VerifyUserAuthenticationRequest();
request.setApplicationExternalId("testApplication");
request.setAuthenticatorType("WEBAUTHN");
request.setUserId("1232312312");
request.setAuthenticationContext("");
// 复制代码运行请自行打印 API 的返回值
try {
VerifyUserAuthenticationResponse response = client.getAcsResponse(request);
System.out.println(response.getAuthenticateResultInfo());
} catch (Exception e) {
//根据e,getCode()判断异常原因
System.out.println(e);
}
}
}
示例代码的maven依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-idaas-doraemon</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<optional>true</optional>
<version>[4.4.9,5.0.0)</version>
</dependency>