我正在制作一个Java应用程序,我正在尝试为需要PKCE(代码交换证明密钥)的API (Etsy.com)实现oauth2授权流。我已经尝试了一段时间了,但在交换OAuth令牌的访问代码时,我一直被困在以下错误:
{"error":"invalid_grant","error_description":"code_verifier is invalid"}
下面是生成初始URL的方法,其中传递sha256编码的验证器:
@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;
}
然后我发送请求以交换令牌的代码为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)+"";
我尝试了许多变化,也尝试在"等"中包装值,但没有任何效果。我是否误解了这个过程?我最初发送一个sha256字符串,当请求oauth2令牌时,我发送的是编码的值。什么好主意吗?
更新:我尝试使用谷歌库来生成哈希,但我仍然得到相同的错误。
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;
code_challenge参数的格式在示例中看起来也不同。
你的挑战生成不正确。验证者的哈希值必须是base64 url编码。
标准说
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
所以这是错误的:
sha256hex = new String(encodedhash);
应该是:
sha256hex = Base64.getUrlEncoder().withoutPadding().encodeToString(encodedhash);
另外,不要在URL中的挑战后面加上引号。
旁注:没有必要转义质询和验证符,因为它们是以url安全的方式编码的