liveBook · Manning

Authentication challenge

As explained in Software Security for Developers

An authentication challenge is a fresh, unpredictable value supplied during an authentication ceremony to bind the response to that specific ceremony and demonstrate possession of a credential.

An authentication challenge is a fresh random value generated by the backend for one WebAuthn login ceremony. The client signs it with the credential’s private key, and the backend verifies the signature with the registered public key. Matching the response to the stored challenge helps prevent replay and phishing attacks. section 15.3 section 15.4

A practical authentication flow uses these steps:

  • Identify the user and retrieve the credential ID associated with the registered public key. section 15.3
  • Generate a 32-byte unpredictable value with a secure random generator. section 15.3 section 15.3.1
  • Store the challenge for that credential and return it to the client with the allowed credential. section 15.3
  • Have the device sign the challenge, then verify the returned signature against the stored challenge and public key. section 15.3 section 15.4

Why freshness matters

A server that accepts a signed response without checking it against the current stored challenge could accept an old response again. Requiring the original challenge from the correct domain ties the signature to the current ceremony. section 15.4

Listing 15.6 The challenge endpoint
@PostMapping("/authenticate/challenge")
public Map<String, Object> getAuthenticationChallenge(
  @RequestBody Map<String, String> request) {
    String username = request.get("username");

       String storedCredentialId = getUser(username);   #1

       byte[] challenge = new byte[32];   #2
    new SecureRandom().nextBytes(challenge);

       store.put(storedCredentialId, challenge);  #3

    Map<String, Object> allowCredential 
    = new HashMap<>();   #4
    allowCredential.put("type", "public-key");
    allowCredential.put("id", storedCredentialId);

    var encChallange = Base64.getEncoder()
    .encodeToString(challenge);   #5

    Map<String, Object> response = new HashMap<>();   #6
    response.put("challenge", encChallange );
    response.put("allowCredentials", List.of(allowCredential));

    return response;
}

The challenge endpoint shows the core implementation: identify the credential, generate 32 random bytes, store them, Base64-encode the value, and return it to the client. section 15.3

Listing 15.7 The authentication endpoint
@PostMapping("/authenticate")
public String authenticate(
  @RequestBody AuthenticationResponse response) {

  String credentialId = response.getId();   #1
  byte[] signature = Base64.getDecoder()
    .decode(response.getResponse().getSignature());
  byte[] authenticatorData = Base64.getDecoder()
    .decode(response.getResponse().getAuthenticatorData());
  byte[] clientDataJSON = Base64.getDecoder()
    .decode(response.getResponse().getClientDataJSON());

  byte[] storedPublicKey  #2
  = getRegiteredPublicKey(credentialId);   #2
  byte[] originalChallenge  #2
  = store.get(credentialId);   #2

  var succss = verifySignatureAndAuthenticate(   #3
      storedPublicKey, 
      orginalChallenge, 
      signature);   

  if (success) return "Authentication successful";
  else throw new UnsuccesfulAuthenticationException();
}

The authentication endpoint demonstrates retrieving the stored challenge and public key before verifying the signed response. section 15.3

How Software Security for Developers covers it

How deep it goes
Explains generation, storage, signing, and verification, but stops short of a production-ready implementation and persistent storage design.
Examples
Uses a 32-byte SecureRandom value, Java challenge and authentication endpoints, and a simulated JavaScript/WebAuthn exercise.
What it assumes
Assumes registration has created a user-linked credential and public key, while explaining authentication through three basic steps.
What you can do after
Build it from scratch by generating a challenge, requesting a signed response, and verifying it against the registered public key.
How it connects
Connects WebAuthn authentication with public-key credentials, private-key signing, origin verification, and replay prevention.

Also explained in

Compare how 2 books explain authentication challenge →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Software Security for Developers. The section links point to the parts of the book it draws on.