Spotify PKCE. Ошибка invalid client secret

Задача в том, чтобы пройти шаги Authorization Code Flow with Proof Key for Code Exchange (PKCE) и получить access-token. На шаге 4 получаю ошибку Invalid client secret. Но сам подход исключает необходимость использования client secret.

Тело post-запроса выглядит так:

code=abc&grant_type=authorization_code&redirect_uri=spotify-sdk%3A%2F%2Fauth&client_id=abc&code_verifier=abc

Шаг 1

Использовать класс PkceUtil, чтобы получить code_verifier и code_challenge

class PkceUtil {

    String generateCodeVerifier(){
        SecureRandom random = new SecureRandom();
        byte[] codeVerifier = new byte[32];
        random.nextBytes(codeVerifier);
        return Base64.encodeToString(codeVerifier, Base64.NO_PADDING).replace("\n", "");
    }

    String generateCodeChallenge(String codeVerifier) {
        byte[] bytes = codeVerifier.getBytes(StandardCharsets.US_ASCII);
        MessageDigest messageDigest = getMessageDigestInstance();
        if (messageDigest != null) {
            messageDigest.update(bytes, 0, bytes.length);
            byte[] digest = messageDigest.digest();
            return Base64.encodeToString(digest, Base64.NO_PADDING).replace("\n", "");
        }
        return "";
    }

    private MessageDigest getMessageDigestInstance(){
        try {
            return MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Шаг 2

С помощью официальной библиотеки android-sdk auth-lib by Spotify отравить запрос на получение code

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

Принять code и отправить запрос на получение access token

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;

    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)
                .build();

        return restAdapter.create(SpotifyAuthService.class);
    }

    public SpotifyAuthService getService() {
        return mSpotifyAuthService;
    }

}

Ответы (0 шт):