AccessKeyFetcher.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #include "AccessKeyFetcher.h"
  2. #include <cstring> // for strrchr
  3. #include <initializer_list> // for initializer_list
  4. #include <map> // for operator!=, operator==
  5. #include <type_traits> // for remove_extent_t
  6. #include <vector> // for vector
  7. #include "BellLogger.h" // for AbstractLogger
  8. #include "CSpotContext.h" // for Context
  9. #include "Logger.h" // for CSPOT_LOG
  10. #include "MercurySession.h" // for MercurySession, MercurySession::Res...
  11. #include "Packet.h" // for cspot
  12. #include "TimeProvider.h" // for TimeProvider
  13. #include "Utils.h" // for string_format
  14. #include "WrappedSemaphore.h"
  15. #ifdef BELL_ONLY_CJSON
  16. #include "cJSON.h"
  17. #else
  18. #include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json
  19. #include "nlohmann/json_fwd.hpp" // for json
  20. #endif
  21. using namespace cspot;
  22. static std::string CLIENT_ID =
  23. "65b708073fc0480ea92a077233ca87bd"; // Spotify web client's client id
  24. static std::string SCOPES =
  25. "streaming,user-library-read,user-library-modify,user-top-read,user-read-"
  26. "recently-played"; // Required access scopes
  27. AccessKeyFetcher::AccessKeyFetcher(std::shared_ptr<cspot::Context> ctx)
  28. : ctx(ctx) {
  29. this->updateSemaphore = std::make_shared<bell::WrappedSemaphore>();
  30. }
  31. bool AccessKeyFetcher::isExpired() {
  32. if (accessKey.empty()) {
  33. return true;
  34. }
  35. if (ctx->timeProvider->getSyncedTimestamp() > expiresAt) {
  36. return true;
  37. }
  38. return false;
  39. }
  40. std::string AccessKeyFetcher::getAccessKey() {
  41. if (!isExpired()) {
  42. return accessKey;
  43. }
  44. updateAccessKey();
  45. return accessKey;
  46. }
  47. void AccessKeyFetcher::updateAccessKey() {
  48. if (keyPending) {
  49. // Already pending refresh request
  50. return;
  51. }
  52. keyPending = true;
  53. CSPOT_LOG(info, "Access token expired, fetching new one...");
  54. std::string url =
  55. string_format("hm://keymaster/token/authenticated?client_id=%s&scope=%s",
  56. CLIENT_ID.c_str(), SCOPES.c_str());
  57. auto timeProvider = this->ctx->timeProvider;
  58. ctx->session->execute(
  59. MercurySession::RequestType::GET, url,
  60. [this, timeProvider](MercurySession::Response& res) {
  61. if (res.fail)
  62. return;
  63. auto accessJSON =
  64. std::string((char*)res.parts[0].data(), res.parts[0].size());
  65. #ifdef BELL_ONLY_CJSON
  66. cJSON* jsonBody = cJSON_Parse(accessJSON.c_str());
  67. this->accessKey =
  68. cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring;
  69. int expiresIn = cJSON_GetObjectItem(jsonBody, "expiresIn")->valueint;
  70. cJSON_Delete(jsonBody);
  71. #else
  72. auto jsonBody = nlohmann::json::parse(accessJSON);
  73. this->accessKey = jsonBody["accessToken"];
  74. int expiresIn = jsonBody["expiresIn"];
  75. #endif
  76. expiresIn = expiresIn / 2; // Refresh token before it expires
  77. this->expiresAt =
  78. timeProvider->getSyncedTimestamp() + (expiresIn * 1000);
  79. updateSemaphore->give();
  80. });
  81. updateSemaphore->twait(5000);
  82. // Mark as not pending for refresh
  83. keyPending = false;
  84. }