Spotify PKCE。错误无效的客户端密码
Posted
技术标签:
【中文标题】Spotify PKCE。错误无效的客户端密码【英文标题】:Spotify PKCE. Error invalid client secret 【发布时间】:2021-03-12 03:34:11 【问题描述】:我需要填写Authorization Code Flow with Proof Key for Code Exchange。在第 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
我对请求的看法也是如此。
步骤 1
使用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;
第二步
使用官方android-sdk auth-lib by Spotify
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();
第 3 步和第 4 步
获取代码并发送请求以进行交换
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 并帮助改造
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;
【问题讨论】:
【参考方案1】:我不熟悉 Spotify Android SDK 库,但从 this issue 来看,它不支持 PKCE 身份验证流程,我不确定当您设置自定义 code_challenge
和 @987654324 时它是否会创建有效请求@参数。
确保此步骤 (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 我仍然建议您尝试使用其他客户端发出此请求,并尝试在接下来的步骤中使用获得的值,以确保您在哪一步遇到问题。
我已经使用 Spotify SDK 成功通过了 PKCE 授权。我已经在我的回答here 中解释过了。它工作得非常好——您可以获得授权令牌并使用它来使用 Web API。但它不支持刷新令牌,这可能是使用 SDK 的方法的问题。尽管如此,它仍然有效,并且可以通过此授权流程。以上是关于Spotify PKCE。错误无效的客户端密码的主要内容,如果未能解决你的问题,请参考以下文章
我可以在没有客户端密码的情况下使用带有 PKCE 的 IdentityServer3 授权代码流吗?
如何在 Kotlin 中使用 PKCE 实现 Spotify 授权代码
Spotify PKCE 授权流程返回“code_verifier 不正确”
在 ReactJS 中的 Spotify API 上为 PKCE 身份验证创建代码验证器和质询
关于OAuth2.0 Authorization Code+PKCE flow在原生客户端(Native App)下集成的思考