我需要完成带有证明密钥的授权代码流以进行代码交换。在第4步中,我得到一个错误400 - bad request {"error":"invalid_request","error_description":"Invalid client secret"}
。
如果是PKCE,为什么需要客户端保密。我怎么了 你有什么主意吗?
身体要求像
code = abc&grant_type = authorization_code&redirect_uri = spotify-sdk%3A%2F%2Fauth&client_id = abc&code_verifier = abc
示例代码验证程序: xeJ7Sx1lyUr0A_DAomzewuGn8vNS2cd3ZF2odDlqHEqeYKpxjnYYhpHxOohoo7lf22VNImGiOy_PE07owmDn2VmTWvdKKQ
示例代码挑战: N_yPRc_VC8JQJz5dYOuvvM-9cJLdAtEjJ9-lh8Xk_qI
我也希望得到同样的结果。
使用PkceUtil
类别
class PkceUtil {
private static final int PKCE_BASE64_ENCODE_SETTINGS = Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE;
String generateCodeVerifier(){
SecureRandom random = new SecureRandom();
byte[] codeVerifier = new byte[40];
random.nextBytes(codeVerifier);
return Base64.encodeToString(codeVerifier, PKCE_BASE64_ENCODE_SETTINGS);
}
String generateCodeChallenge(String codeVerifier) {
byte[] bytes = codeVerifier.getBytes(StandardCharsets.UTF_8);
MessageDigest messageDigest = getMessageDigestInstance();
if (messageDigest != null) {
messageDigest.update(bytes);
byte[] digest = messageDigest.digest();
return Base64.encodeToString(digest, PKCE_BASE64_ENCODE_SETTINGS);
}
return "";
}
private MessageDigest getMessageDigestInstance(){
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
使用Spotify的官方android-sdk auth-lib
private AuthorizationRequest getAuthRequestCode() {
PkceUtil pkceUtil = new PkceUtil();
codeVerifier = pkceUtil.generateCodeVerifier();
codeChallenge = pkceUtil.generateCodeChallenge(codeVerifier);
return new AuthorizationRequest.Builder(CLIENT_ID, AuthorizationResponse.Type.CODE, getRedirectUri())
.setShowDialog(false)
.setScopes(SCOPE)
.setCustomParam("code_challenge_method", "S256")
.setCustomParam("code_challenge", codeChallenge)
.build();
}
private String getRedirectUri() {
return Uri.parse(REDIRECT_URI).toString();
}
获取代码并发送请求以进行交换
private void onAuthResponse(int resultCode, Intent intent){
AuthorizationResponse response = AuthorizationClient.getResponse(resultCode, intent);
switch (response.getType()) {
case TOKEN:
break;
case CODE:
SpotifyAuthApi api = new SpotifyAuthApi();
SpotifyAuthService spotify = api.getService();
Map<String, Object> map = new HashMap<>();
map.put("client_id", CLIENT_ID);
map.put("grant_type", "authorization_code");
map.put("code", response.getCode());
map.put("redirect_uri", getRedirectUri());
map.put("code_verifier", codeVerifier);
spotify.getAccessToken(map, new Callback<AuthorizationResponse>() {
@Override
public void success(AuthorizationResponse authorizationResponse, Response response) {
}
@Override
public void failure(RetrofitError error) {
// Error 400 - bad request
}
});
break;
case ERROR:
break;
default:
}
}
为了发送请求,请使用自己的AuthApi和AuthService以及帮助Retrofit
public interface SpotifyAuthService {
@POST("/api/token")
@FormUrlEncoded
AuthorizationResponse getAccessToken(@FieldMap Map<String, Object> params);
@POST("/api/token")
@FormUrlEncoded
void getAccessToken(@FieldMap Map<String, Object> params, Callback<AuthorizationResponse> callback);
}
public class SpotifyAuthApi {
private static final String SPOTIFY_ACCOUNTS_ENDPOINT = "https://accounts.spotify.com/";
private final SpotifyAuthService mSpotifyAuthService;
private class WebApiAuthenticator implements RequestInterceptor {
@Override
public void intercept(RequestFacade request) {
request.addHeader("content-type", "application/x-www-form-urlencoded");
}
}
public SpotifyAuthApi() {
Executor httpExecutor = Executors.newSingleThreadExecutor();
MainThreadExecutor callbackExecutor = new MainThreadExecutor();
mSpotifyAuthService = init(httpExecutor, callbackExecutor);
}
private SpotifyAuthService init(Executor httpExecutor, Executor callbackExecutor) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.BASIC)
.setExecutors(httpExecutor, callbackExecutor)
.setEndpoint(SPOTIFY_ACCOUNTS_ENDPOINT)
.setRequestInterceptor(new SpotifyAuthApi.WebApiAuthenticator())
.build();
return restAdapter.create(SpotifyAuthService.class);
}
public SpotifyAuthService getService() {
return mSpotifyAuthService;
}
}
我不熟悉Spotify Android SDK库,但从此问题来看,它不支持PKCE身份验证流程,并且不确定在设置自定义code_challenge
和code_challenge_method
参数时它是否创建有效的请求。
确保此步骤(2)起作用,否则,授权端点假定你使用的是正常的授权码流,并且期望client_secret
(在步骤4中)。
根据日志,来自库的请求是正确的:
com.spotify.sdk.android.auth.LoginActivity: Spotify Auth starting with the request [https://accounts.spotify.com/authorize?client_id=abc&response_type=code&redirect_uri=spotify-sdk%3A%2F%2Fauth&show_dialog=false&utm_source=spotify-sdk&utm_medium=android-sdk&utm_campaign=android-sdk&scope=playlist-read-private&code_challenge_method=S256&code_challenge=abc]
至少所有字段都在那里。@Viewed我仍然建议您尝试使用其他客户端发出此请求,并尝试在后续步骤中使用获取的值以确保在哪一步出现问题。