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.