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