webauthn::WebAuthn method reg attestation_verify (public)

 <instance of webauthn::WebAuthn[i]> reg attestation_verify \
    [ -st st ] [ -req req ]

Defined in packages/webauthn/tcl/webauthn-procs.tcl

Verify a WebAuthn registration response (attestation) against stored state. This method validates the incoming credential creation response from navigator.credentials.create() for the current registration ceremony. It checks required fields, verifies the clientDataJSON (type, challenge, origin), decodes and parses the attestationObject (CBOR), and extracts credential data (credential ID and public key) for subsequent storage.

Switches:
-st (optional)
Registration state dict as created by /webauthn/reg/options (challenge, origin, return_url, user_id, ...).
-req (optional)
Parsed client response dict containing "response" fields, including clientDataJSON and attestationObject.

Testcases:
No testcase defined.
Source code:
set return_url        [dict get $st return_url]
set user_id           [dict get $st user_id]

if {![dict exists $req response clientDataJSON]
    || ![dict exists $req response attestationObject]} {
    throw {validation fields-missing} "missing required fields"
}

# ---- clientDataJSON
:assert_clientdata_json  -clientData_raw      [dict get $req response clientDataJSON]  -expected_type       "webauthn.create"  -expected_challenge  [dict get $st challenge]  -expected_origin     [dict get $st origin]

# ---- attestationObject (CBOR) -> authData -> credId + COSE_Key
set attObj_b64u [dict get $req response attestationObject]
set attObj_bin [ns_base64urldecode -binary -- $attObj_b64u]

try {
    set ao [ns_cbor decode -binary -encoding binary $attObj_bin]
} on error {e} {
    throw {validation attobj-cbor} "bad attestationObject CBOR: $e"
}

if {![dict exists $ao fmt] || ![dict exists $ao authData]} {
    throw {validation attobj-invalid} "attestationObject missing fmt/authData"
}

set fmt [dict get $ao fmt]
if {$fmt ne "none"} {
    ns_log notice "registration attestation fmt=$fmt"
}

set authData [dict get $ao authData]
if {[string length $authData] < 55} {
    throw {validation authdata-invalid} "authData too short"
}

set rpIdHash [string range $authData 0 31]
:assert_rpidhash -rpIdHash $rpIdHash -rpId ${:rp_id} -context "attestation"

binary scan [string range $authData 32 32] cu flags
if {($flags & 0x40) == 0} {
    throw {validation attesteddata-missing} "no attested credential data"
}

binary scan [string range $authData 33 36] Iu signCount

# fixed offsets from WebAuthn authData layout
set aaguid [string range $authData 37 37+15]
binary scan [string range $authData 53 53+1] S credIdLen

if {$credIdLen <= 0 || (55+$credIdLen) > [string length $authData]} {
    throw {validation credidlen-invalid} "credentialId length out of range"
}

set credId  [string range $authData 55 54+$credIdLen]
set coseKey [string range $authData 55+$credIdLen end]

try {
    set cose [ns_cbor decode -binary -encoding binary $coseKey]
} on error {e} {
    throw {validation cose-cbor} "bad COSE_Key CBOR: $e"
}

# Basic COSE sanity (ES256 / P-256)
if {![dict exists $cose 3] || [dict get $cose 3] != -7} {
    throw {validation alg-unsupported} "unsupported COSE alg (expected -7 ES256)"
}
if {![dict exists $cose 1] || [dict get $cose 1] != 2} {
    throw {validation keytype-unsupported} "unsupported COSE kty (expected 2 EC2)"
}
if {![dict exists $cose -1] || [dict get $cose -1] != 1} {
    throw {validation curve-unsupported} "unsupported COSE crv (expected 1 P-256)"
}

# Build DB values
set credential_id [ns_base64urlencode -binary -- $credId]
set public_key [dict create  format cose  fmt $fmt  aaguid_b64u [ns_base64urlencode -binary -- $aaguid]  alg [dict get $cose 3]  crv [dict get $cose -1]  cose_b64u [ns_base64urlencode -binary -- $coseKey]  sign_count $signCount  rp_id ${:rp_id}]

return [dict create  user_id $user_id  return_url $return_url  credential_id $credential_id  public_key $public_key  origin [dict get $st origin]  sign_count $signCount]
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: