1 #include "lib/spotify/auth.hpp"
2
auth(lib::settings & settings,const lib::http_client & http_client)3 lib::spt::auth::auth(lib::settings &settings, const lib::http_client &http_client)
4 : settings(settings),
5 http_client(http_client)
6 {
7 }
8
url(const std::string & client_id,const std::string & redirect_url)9 auto lib::spt::auth::url(const std::string &client_id,
10 const std::string &redirect_url) -> std::string
11 {
12 // Scopes for request, for clarity
13 std::vector<std::string> scopes = {
14 "playlist-read-collaborative",
15 "playlist-read-private",
16 "playlist-modify-private",
17 "playlist-modify-public",
18 "user-follow-read",
19 "user-follow-modify",
20 "user-library-modify",
21 "user-library-read",
22 "user-modify-playback-state",
23 "user-read-currently-playing",
24 "user-read-playback-state",
25 "user-read-private",
26 "user-read-recently-played",
27 "streaming",
28 "user-read-email",
29 "user-top-read"
30 };
31
32 // Prepare url
33 return lib::fmt::format("https://accounts.spotify.com/authorize"
34 "?client_id={}&response_type=code"
35 "&redirect_uri={}&scope={}",
36 client_id, redirect_url, lib::strings::join(scopes, "%20"));
37 }
38
get(const std::string & code,const std::string & redirect_url,const std::string & id,const std::string & secret)39 auto lib::spt::auth::get(const std::string &code, const std::string &redirect_url,
40 const std::string &id, const std::string &secret) -> std::string
41 {
42 if (code.empty())
43 {
44 return "No code specified";
45 }
46
47 // Prepare form to send
48 auto post_data = lib::fmt::format("grant_type=authorization_code&code={}&redirect_uri={}",
49 code, redirect_url);
50
51 // Prepare request
52 lib::headers headers = {
53 {"Content-Type", "application/x-www-form-urlencoded"},
54 {"Authorization", lib::fmt::format("Basic {}",
55 lib::base64::encode(lib::fmt::format("{}:{}", id, secret)))},
56 };
57
58 // Send request
59 auto reply = http_client.post("https://accounts.spotify.com/api/token",
60 headers, post_data);
61
62 nlohmann::json json;
63 try
64 {
65 json = nlohmann::json::parse(reply);
66 }
67 catch (const std::exception &e)
68 {
69 return e.what();
70 }
71
72 if (json.contains("error_description"))
73 {
74 return json.at("error_description").get<std::string>();
75 }
76
77 const auto &access_token = json.at("access_token");
78 const auto &refresh_token = json.at("refresh_token");
79 if (access_token.is_null() || refresh_token.is_null())
80 {
81 return "No access token";
82 }
83
84 // Save access/refresh token
85 access_token.get_to(settings.account.access_token);
86 refresh_token.get_to(settings.account.refresh_token);
87
88 // Everything hopefully went fine
89 return std::string();
90 }
91