我关注了WWDC 2022密钥视频,我正试图在iOS中为我的服务注册密钥,如视频中所述。
以下是我从服务器获得质询,然后使用ASAuthorizationPlatformPublicKeyCredentialProvider生成密钥的函数。
func signUpWith(userName: String, anchor: ASPresentationAnchor) {
self.authenticationAnchor = anchor
self.userName = userName
let publicKeyCredentialProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: self.domain)
// Fetch the challenge from the server. The challenge needs to be unique for each request.
// The userID is the identifier for the user's account.
var urlRequst = URLRequest(url: URL(string: "https://<domain>/registration")!)
urlRequst.httpMethod = "POST"
urlRequst.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let httpBody = try JSONSerialization.data(withJSONObject: ["registration": ["username": userName, "nickname": userName]], options: [])
urlRequst.httpBody = httpBody
} catch let error {
print(error)
}
let urlSession = URLSession(configuration: .default)
var task: URLSessionDataTask?
task = urlSession.dataTask(with: urlRequst) { data, response, error in
let challengeJson = try? JSONDecoder().decode(Challenge.self, from: data!)
let challengeString = challengeJson!.challenge
let userIdString = challengeJson!.user.id
let challengeData = Data(challengeString.utf8)
let userID = Data(userIdString.utf8)
let registrationRequest = publicKeyCredentialProvider.createCredentialRegistrationRequest(challenge: challengeData,
name: userName, userID: userID)
// Use only ASAuthorizationPlatformPublicKeyCredentialRegistrationRequests or
// ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequests here.
let authController = ASAuthorizationController(authorizationRequests: [ registrationRequest ] )
authController.delegate = self
authController.presentationContextProvider = self
authController.performRequests()
self.isPerformingModalReqest = true
}
task?.resume()
}
这是有效的,我能够获得挑战,并在iPhone上启动本地生物特征验证,为给定用户名的服务生成密钥。
以下是从服务器收到的挑战的控制台打印:-
{
"challenge":"fS-mfyjb3_sBjgU2X3xp99jxdFcNVq2l1Yn-097FWL8",
"timeout":120000,
"rp":{
"name":"Passkeys demo app"
},
"user":{
"name":"letsbondiway",
"id":"EU1BXzOQUYAE0_WbIM1LEdbhE2Y7tA-o8-gl6P27mAe_cV-Q3xKxFovyOV5cY_0kJm1z_mvOHft1AKE2AaW1sQ",
"displayName":"letsbondiway"
},
"pubKeyCredParams":[
{
"type":"public-key",
"alg":-7
},
{
"type":"public-key",
"alg":-37
},
{
"type":"public-key",
"alg":-257
}
]
}
但是,在delegate方法中,当我解码cliendDataJSON对象时,挑战值是不同的。
以下是委托方法处理:-
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
let logger = Logger()
switch authorization.credential {
case let credentialRegistration as ASAuthorizationPlatformPublicKeyCredentialRegistration:
logger.log("A new passkey was registered: (credentialRegistration)")
// Verify the attestationObject and clientDataJSON with your service.
// The attestationObject contains the user's new public key to store and use for subsequent sign-ins.
let attestationObject = credentialRegistration.rawAttestationObject
let clientDataJSON = credentialRegistration.rawClientDataJSON
let credentialId = credentialRegistration.credentialID
print(String(data: clientDataJSON, encoding: .utf8) as Any)
// After the server verifies the registration and creates the user account, sign in the user with the new account.
didFinishSignIn()
case let credentialAssertion as ASAuthorizationPlatformPublicKeyCredentialAssertion:
logger.log("A passkey was used to sign in: (credentialAssertion)")
// Verify the below signature and clientDataJSON with your service for the given userID.
// let signature = credentialAssertion.signature
// let clientDataJSON = credentialAssertion.rawClientDataJSON
// let userID = credentialAssertion.userID
// After the server verifies the assertion, sign in the user.
didFinishSignIn()
case let passwordCredential as ASPasswordCredential:
logger.log("A password was provided: (passwordCredential)")
// Verify the userName and password with your service.
// let userName = passwordCredential.user
// let password = passwordCredential.password
// After the server verifies the userName and password, sign in the user.
didFinishSignIn()
default:
fatalError("Received unknown authorization type.")
}
isPerformingModalReqest = false
}
委托方法中的打印输出:-
{
"type":"webauthn.create",
"challenge":"ZlMtbWZ5amIzX3NCamdVMlgzeHA5OWp4ZEZjTlZxMmwxWW4tMDk3RldMOA",
"origin":"https://<domain>"
}
我在这里做错了什么?为什么挑战价值观不同?
在将挑战传递给API之前,我认为您缺少一个base64url-decode。由此产生的clientDataJSON中的挑战是base64url本身编码:
ZlMtbWZ5amIzX3NCamdVMlgzeHA5OWp4ZEZjTlZxMmwxWW4tMDk3RldMOA
如果我们基于64url解码,我们得到:
fS-mfyjb3_sBjgU2X3xp99jxdFcNVq2l1Yn-097FWL8
这是来自服务器的挑战。因此,您应该自己对来自服务器的挑战进行64url-decode编码,并将二进制挑战传递到API。然后,clientDataJSON中的值应该与您期望的值相匹配。