2

I'm making a Java App where I'm trying to implement an OAuth2-authorization flow for an API (Etsy.com) that requires PKCE (Proof Key for Code Exchange). I've been trying for some time now but I keep being stuck at the following error when exchanging the access code for the OAuth token:

{"error":"invalid_grant","error_description":"code_verifier is invalid"}

Here's the method for generating the initial URL where the sha256-encoded verifier is passed:

@Override
public String getOAuth2AuthorizationUrl() {

    String sha256hex = null;
    try {
        if(_currentVerifier == null)
            _currentVerifier = PkceUtil.generateCodeVerifier();

        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] encodedhash = digest.digest(
                _currentVerifier.getBytes(StandardCharsets.UTF_8));
        sha256hex = new String(encodedhash);
    } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }

    UnicodeEscaper basicEscaper = new PercentEscaper("-", false);
    System.out.println("sha256="+sha256hex);
    System.out.println("veri="+_currentVerifier);

    String oAuth2AuthorizationUrl = super.getOAuth2AuthorizationUrl();
    var authUrl = oAuth2AuthorizationUrl
            +"&state=asdf&code_challenge_method=S256&code_challenge='"
            + basicEscaper.escape(sha256hex)+"'";
    return authUrl;
}

Then I'm sending the request to exchange the code for the token as application/x-www-form-urlencoded POST-request:

    UnicodeEscaper basicEscaper = new PercentEscaper("-", false);
 var params = "grant_type=authorization_code&client_id="
            +token.getOwner().clientId+"&redirect_uri="
            +basicEscaper.escape("https://localhost")
            +"&code="+accessCode+"&code_verifier="+basicEscaper.escape(_currentVerifier)+"";

I tried many variations, also tried wrapping the values in '' etc. but nothing worked. Have I misunderstood this process? I'm sending a sha256 string initially and when requesting the OAuth2-token I'm sending the value that was encoded. Any ideas?

Update: I tried using Googles library for generating the hash, but I'm still getting the same error.

Hasher hasher = Hashing.sha256().newHasher();
    hasher.putString(CODE_VERIFIER, Charsets.UTF_8);
    HashCode sha256 = hasher.hash();

    System.out.println("veri="+AppUtils.encodeBase64(AppUtils.toHexString(sha256.asBytes())));

    String oAuth2AuthorizationUrl = super.getOAuth2AuthorizationUrl();

    var authUrl = oAuth2AuthorizationUrl
            +"&state=asdf&code_challenge_method=S256&code_challenge="+AppUtils.encodeBase64(AppUtils.toHexString(sha256.asBytes()));
    return authUrl;

Also the format of the code_challenge parameter looks different in the samples.

Maxim
  • 227
  • 2
  • 14

1 Answers1

1

Your challenge generation is incorrect. The hash of the verifier has to be base64 url encoded.

The standard says

code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

So this is wrong:

sha256hex = new String(encodedhash);

It should be:

sha256hex = Base64.getUrlEncoder().withoutPadding().encodeToString(encodedhash);

Also, do not put quotes around the challenge in the URL.

Side note: It is not necessary to escape the challenge and verifier because they are encoded in a url safe way

teppic
  • 7,051
  • 1
  • 29
  • 35