The client makes a random verifier, sends its SHA-256 (the challenge) in the authorization request and the verifier itself in the token request. A stolen code is useless without the verifier.