【问题标题】:Spotify PKCE. Error invalid client secretSpotify PKCE。错误无效的客户端密码
【发布时间】: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;
    }

}

【问题讨论】:

    标签: java android request retrofit spotify


    【解决方案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&amp;response_type=code&amp;redirect_uri=spotify-sdk%3A%2F%2Fauth&amp;show_dialog=false&amp;utm_source=spotify-sdk&amp;utm_medium=android-sdk&amp;utm_campaign=android-sdk&amp;scope=playlist-read-private&amp;code_challenge_method=S256&amp;code_challenge=abc] 至少所有字段都在那里。
    • @Viewed 我仍然建议您尝试使用其他客户端发出此请求,并尝试在接下来的步骤中使用获得的值,以确保您在哪一步遇到问题。
    • 我已经使用 Spotify SDK 成功通过了 PKCE 授权。我已经在我的回答here 中解释过了。它工作得非常好——您可以获得授权令牌并使用它来使用 Web API。但它不支持刷新令牌,这可能是使用 SDK 的方法的问题。尽管如此,它仍然有效,并且可以通过此授权流程。
    猜你喜欢
    • 2020-09-03
    • 2017-01-18
    • 2020-07-25
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 2016-03-15
    • 2022-11-10
    相关资源
    最近更新 更多