{"error": "invalid_client"} из Spotify Web Api при попытке обновить токен sh - PullRequest
1 голос
/ 13 января 2020

Я новичок ie в терминах как на Qt, так и на C ++, но я пытался немного поиграть с web-интерфейсом spotify для проекта uni. К сожалению, я застрял при публикации моего refre sh токена в api, чтобы получить новый токен доступа. Каждый раз, когда я делаю это, я получаю ответ {"error": "invalid_client"}. Я убедился, что мой идентификатор клиента и секрет клиента верны. Я думаю, что где-то в моем заголовке есть ошибка, но я пробовал несколько вещей, и это мало что помогло.

Вот мой код:

void MainWindow::on_pushButton_2_clicked(){

    QNetworkAccessManager * manager = new QNetworkAccessManager(this);

    QUrl url("https://accounts.spotify.com/api/token");
    QNetworkRequest request(url);
    QString header = "my_client_id:my_client_secret";
    QByteArray ba;
    ba.append(header);
    ui->teOutput->appendPlainText(ba.toBase64());
    QString full_header= "Authorization: Basic " + ba.toBase64();
    //ui->teOutput->appendPlainText(full_header);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    request.setHeader(QNetworkRequest::ContentDispositionHeader, full_header);

    QUrlQuery params;
    params.addQueryItem("grant_type", "refresh_token");
    params.addQueryItem("refresh_token", "here_is_my_refresh_token");

    connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onFinish(QNetworkReply *)));
    manager->post(request, params.query().toUtf8());
    //ui->teOutput->appendPlainText(params.query().toUtf8());
}

void MainWindow::onFinish(QNetworkReply *rep)
{
    QString data = rep->readAll();
    ui->teOutput->appendPlainText(data);
}

А вот как мой запрос должен выглядеть (как и предполагалось, я проверил, что с помощью cUrl)

curl -X "POST" -H "Authorization: Basic xxxxxxxxxxx" -d grant_type=refresh_token -d refresh_token=xxxxxxxxxxxxx https://accounts.spotify.com/api/token

1 Ответ

0 голосов
/ 13 января 2020

Команда -H "Authorization: Basic xxxxxxxxxxx" эквивалентна:

request.setRawHeader("Authorization", "Basic xxxxxxxxxxx"); 

Таким образом, вы должны использовать вместо request.setHeader(QNetworkRequest::ContentDispositionHeader, full_header);.


Хотя нет необходимости реализовывать все логи c, поскольку Spotify API использует OAuth2, который Qt реализует в модуле Qt Network Authorization .

При запросе или обновлении токена должен изменяться только заголовок QNetworkRequest, поскольку он не является частью стандарт Oauth2.

constants.h

#ifndef CONSTANTS_H
#define CONSTANTS_H

#include <QByteArray>
#include <QUrl>

namespace Constants {
    const QByteArray SPOTIFY_CLIENT_ID = "xxxxxxxx";
    const QByteArray SPOTIFY_CLIENT_SECRET = "yyyyyyyy";

    const QUrl SPOTIFY_AUTHORIZATION_URL = QUrl("https://accounts.spotify.com/authorize");
    const QUrl SPOTIFY_ACCESSTOKEN_URL = QUrl("https://accounts.spotify.com/api/token");
}

#endif // CONSTANTS_H

networkaccessmanager.h

#ifndef NETWORKACCESSMANAGER_H
#define NETWORKACCESSMANAGER_H

#include <QNetworkAccessManager>

class NetworkAccessManager : public QNetworkAccessManager
{
public:
    using QNetworkAccessManager::QNetworkAccessManager;
protected:
    QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData);
};

#endif // NETWORKACCESSMANAGER_H

networkaccessmanager . cpp

#include "networkaccessmanager.h"
#include "constants.h"
#include <QtGlobal>

QNetworkReply *NetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
                                                   const QNetworkRequest &request,
                                                   QIODevice *outgoingData)
{
    QNetworkRequest r(request);
    if(r.url() == Constants::SPOTIFY_ACCESSTOKEN_URL)
        r.setRawHeader("Authorization",
                       "Basic " +
                       QByteArray(Constants::SPOTIFY_CLIENT_ID + ":" + Constants::SPOTIFY_CLIENT_SECRET).toBase64());
    return QNetworkAccessManager::createRequest(op, r, outgoingData);
}

spotifywrapper.h

#ifndef SPOTIFYWRAPPER_H
#define SPOTIFYWRAPPER_H

#include <QOAuth2AuthorizationCodeFlow>
#include <QObject>

class SpotifyWrapper : public QObject
{
    Q_OBJECT
public:
    explicit SpotifyWrapper(QObject *parent = nullptr);
    QNetworkReply *me();
public Q_SLOTS:
    void grant();
Q_SIGNALS:
    void authenticated();
private:
    QOAuth2AuthorizationCodeFlow oauth2;
};

#endif // SPOTIFYWRAPPER_H

spotifywrapper. cpp

#include "spotifywrapper.h"
#include "networkaccessmanager.h"
#include "constants.h"

#include <QDesktopServices>
#include <QOAuthHttpServerReplyHandler>
#include <QUrlQuery>

SpotifyWrapper::SpotifyWrapper(QObject *parent) : QObject(parent)
{
    QOAuthHttpServerReplyHandler * replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
    replyHandler->setCallbackPath("callback");
    oauth2.setNetworkAccessManager(new NetworkAccessManager(this));
    oauth2.setReplyHandler(replyHandler);
    oauth2.setAuthorizationUrl(Constants::SPOTIFY_AUTHORIZATION_URL);
    oauth2.setAccessTokenUrl(Constants::SPOTIFY_ACCESSTOKEN_URL);
    oauth2.setClientIdentifier(Constants::SPOTIFY_CLIENT_ID);
    oauth2.setScope("user-read-private user-read-email");
    oauth2.setState("34fFs29kd09");
    connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
            &QDesktopServices::openUrl);
    connect(&oauth2, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](
            QAbstractOAuth::Status status) {
        if (status == QAbstractOAuth::Status::Granted){
            emit authenticated();
        }
    });
}

void SpotifyWrapper::grant()
{
    oauth2.grant();
}

QNetworkReply *SpotifyWrapper::me()
{
    return oauth2.get(QUrl("https://api.spotify.com/v1/me"));
}

main. cpp

#include "spotifywrapper.h"

#include <QNetworkReply>
#include <QGuiApplication>
#include <QTimer>

int main(int argc, char *argv[])
{
    QGuiApplication a(argc, argv);
    SpotifyWrapper wrapper;
    wrapper.grant();
    QObject::connect(&wrapper, &SpotifyWrapper::authenticated, [&wrapper](){
        qDebug() << "authenticated";
        QNetworkReply *reply = wrapper.me();
        QObject::connect(reply, &QNetworkReply::finished, [=]() {
                reply->deleteLater();
                if (reply->error() != QNetworkReply::NoError) {
                    qDebug() << reply->errorString();
                    return;
                }
                qDebug() << reply->readAll();
                QTimer::singleShot(1000, &QCoreApplication::quit);
        });
    });
    return a.exec();
}

Вы также должны установить в качестве URI перенаправления в НАСТРОЙКАХ вашего приложения значение "http://127.0.0.1: 1337 / callback":

enter image description here

Полный пример можно найти здесь

...