Calling all developers! Today, we’re kicking off our first-ever post in our new technical blog series specifically designed for our developer community. Each month, we will be selecting a new technical topic to cover in more depth.
To start our series, we dive into the
clientDataJSON object as part of the Web Authentication or WebAuthn specification. WebAuthn is an exciting standard that has garnered a lot of interest, but it can often feel complicated to get started.
WebAuthn defines a client/server ceremony API performing user registration and authentication. For registration, the user, via a client (web browser or mobile app), requests to register a hardware authenticator with a server. For authentication, that user, via a client app, attempts to login to a server with that previously registered authenticator. During these two ceremonies, there’s data passed back and forth between the client and the server.
clientDataJSON object is key to the WebAuthn API data exchange. If you are building a desktop or mobile application, the building and encoding of the
clientDataJSON object needs to be done using a library or SDK.
First, let’s go over the high-level aspects of WebAuthn and then we can dive into details about what the
clientDataJSON object is, its purpose and its attributes, and finally explain encoding and decoding of this object.
What is WebAuthn?
Web Authentication, or WebAuthn, is a W3C-recommended specification that defines an API for enabling the creation and use of public key-based credentials, for the purpose of strongly authenticating users. See the W3C Specification for more information.
The idea behind WebAuthn is to rid the world of password authentication (something you know) by replacing it with public key authentication (something you have).
For password authentication, a user generates a password which is passed to the server, where it is stored in a database. During user authentication, the user-generated password is sent to the server for validation against the stored password. If the password matches, the user is authenticated and can access the service offerings or features.
For WebAuthn public key authentication, strong hardware-backed public/private-key credentials are created and stored by an authenticator, such as a YubiKey, during registration. The private key is securely stored on the authenticator and is never shared, while the server stores the public key portion in the database.
During user authentication, the server sends pseudo randomly generated challenges to the client for the authenticator to sign. The signature, which takes a hash of the
clientDataJSON along with the
authenticatorData, is signed over by the private key. This signature proves possession of the private key and assurance that the challenge, relying party (RP) ID, and origin were not tampered with, all without ever sharing the private key or requiring the user to provide a static password. Replay attacks are prevented by the pseudo-randomness of the challenge. Phishing attacks are prevented by this signing of the challenge with the private key that is scoped to the RP ID (domain). In addition to measures to counter replay and phishing attacks, the web authentication API also prevents compromised credentials (username + password) in that a password is never passed to or stored by the RP, hence the term “passwordless”.
What is clientDataJSON?
At a high level, the WebAuthn specification is really just an exchange of challenges and responses performed during two types of ceremonies; registration and authentication. The
clientDataJSON object, always populated by the client (browser or app), is sent in response to the RP server during registration and authentication.
The object, populated by the client, has three required properties: a type, a challenge, and an origin. The type can be either “
webauthn.create” for a registration response or “
webauthn.get” for an authentication response. The challenge value is the actual challenge that was sent by the RP during the create or get ceremony. The origin contains the effective domain name of the endpoint to which the client is connecting during the WebAuthn registration or authentication.
Now that we know about the properties, let’s find out the purpose of each property and how these are integrated to control the Web Authentication flow.
clientDataJSON Use Cases
clientDataJSON is used to determine the current state or flow of the WebAuthn ceremony. The type attribute tells the RP whether this client data is a registration or authentication response to a server challenge.
The most important responsibility of the
clientDataJSON is storing the effective domain of the connected client. In the WebAuthn API spec, the client browser or application is responsible for capturing the effective domain of the connected endpoint and storing it in the origin attribute during the registration (create) and authentication (get) ceremony. The public keypair generated by the authenticator is considered to be “origin-based”, which means the keypair can only be used to authenticate a user when the client is connected to the same domain (origin) endpoint to which it was originally connected (or matches a subset of the server domain) at the time when the keypair was generated. I’ll go into this in more detail later.
The last responsibility of the
clientDataJSON is to capture the cryptographic challenge sent by the RP during registration or authentication. The challenge is randomly generated by the RP and sent to the client during a challenge. The client captures the challenge in the challenge attribute and passes this back to the server.
clientDataJSON object (after decoding) has the following properties:
|type||Contains a string with one of two values:
Required Value(s): “
|challenge||The base64url encoded version of the cryptographic challenge sent from the relying party’s server (RP). The original challenge value is passed via the relying party (RP) through
|origin||The effective domain of the requester given by the client/browser to the authenticator.||Required|
Encoding and Decoding clientDataJSON Properties
With only three main attributes, the
clientDataJSON object is pretty straightforward; however, according to the W3C spec, the JSON string is converted into an
ArrayBuffer before being transported back to the RP and then back to a string on the server side before validation. The
ArrayBuffer is being used for efficiency and optimal performance when speaking binary to the authenticators.
The conversion to and from an
ArrayBuffer is the most confusing for developers. The good news is, for most WebAuthn solutions, developers can rely on a Web Authentication API supporting web browser as the client to handle the interaction with the external authenticator. Those browsers have already implemented the client API requirements using the FIDO2 Client to Authenticator Protocol (CTAP) specification. CTAP is an application layer protocol used for communication between a client (browser) or a platform (operating system) with an external authenticator such as the YubiKey.
On the server side, the RP receives the object as an
ArrayBuffer and must be decoded and parsed.
Here’s what the hashed
clientDataJSON object within the client response looks like when received by the relying party during registration:
Here’s what the parsed
clientDataJSON object looks as a JSON string:
In the examples below, the server converts the
ByteArray to a JSON object and then parses and validates the data.
Here’s a Java example of how the Yubico Java Server demo handles the
Relying Party Validation of WebAuthn clientDataJSON
Once the RP receives the registration or authentication response from the client and converts the
ByteArray to a JSON object, it’s ready to parse and validate the three attributes. At this point, the server can validate any of the attributes in any order.
The origin value is the most important validation. The client browser or app determined the endpoint/domain during the request for authentication to the server. The server must then validate that the “
origin” string matches at least a subset of the valid domain string of the RP as part of the Relying Party Identifier (RP ID).
clientDataJSON consists of only three required attributes but plays a critical role in the Web Authentication flow between the client and server. In this post we learned that this object is populated by the client during the registration and authentication flows in order to determine the type of ceremony (registration or authentication), the origin of the connected client, and the current challenge from the server. The data is transported as an
ArrayBuffer by the client and then decoded and parsed by the server. The RP can reject any authentication attempts if the client object is not encoded properly, contains an incorrect challenge, or the client origin does not match the domain (RP identifier) associated with the JSON string.
Building and encoding the
clientDataJSON object is the client responsibility, but that work is typically handled for you by a web browser that supports Web Authentication. However, if you are building a desktop or mobile application, that work will need to be done using a library or SDK of your choice following the Web Authentication API structure as defined by the W3C spec.
Yubico WebAuthn Developer Guide
W3C WebAuthn Spec
Mozilla MDN Web Docs
Yubico iOS SDK – WebAuthn Client
Github WebAuthn API wrapper – WebAuthn API wrapper that translates to/from pure JSON using base64url
Yubico WebAuthn Server (Java)