diff --git a/htdocs/admin.html b/htdocs/admin.html index 0198ba78..3e5bdaf2 100644 --- a/htdocs/admin.html +++ b/htdocs/admin.html @@ -213,6 +213,7 @@

Spotify Web API: access authorized for {{ spotify.webapi_user }}

+

Please reauthorize Web API access to grant forked-daapd the following additional access rights: {{ spotify_missing_scope | join }}

Reauthorize Web API access
diff --git a/htdocs/admin/js/forked-daapd.js b/htdocs/admin/js/forked-daapd.js index 0404f75f..de51bb76 100644 --- a/htdocs/admin/js/forked-daapd.js +++ b/htdocs/admin/js/forked-daapd.js @@ -24,6 +24,15 @@ var app = new Vue({ this.loadLastfm(); }, + computed: { + spotify_missing_scope () { + if (this.spotify.webapi_token_valid && this.spotify.webapi_granted_scope && this.spotify.webapi_required_scope) { + return this.spotify.webapi_required_scope.split(' ').filter(scope => this.spotify.webapi_granted_scope.indexOf(scope) < 0) + } + return [] + } + }, + methods: { loadConfig: function() { axios.get('/api/config').then(response => { diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 69c9cd3b..e3052e97 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -850,6 +850,8 @@ jsonapi_reply_spotify(struct httpd_request *hreq) json_object_object_add(jreply, "webapi_token_valid", json_object_new_boolean(webapi_info.token_valid)); safe_json_add_string(jreply, "webapi_user", webapi_info.user); safe_json_add_string(jreply, "webapi_country", webapi_info.country); + safe_json_add_string(jreply, "webapi_granted_scope", webapi_info.granted_scope); + safe_json_add_string(jreply, "webapi_required_scope", webapi_info.required_scope); spotifywebapi_access_token_get(&webapi_token); safe_json_add_string(jreply, "webapi_token", webapi_token.token); diff --git a/src/spotify_webapi.c b/src/spotify_webapi.c index 56b7c2b3..e59e03c6 100644 --- a/src/spotify_webapi.c +++ b/src/spotify_webapi.c @@ -96,6 +96,7 @@ struct spotify_playlist // Credentials for the web api static char *spotify_access_token; static char *spotify_refresh_token; +static char *spotify_granted_scope; static char *spotify_user_country; static char *spotify_user; @@ -118,8 +119,11 @@ static bool scanning; // Endpoints and credentials for the web api static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789"; static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239"; +static const char *spotify_scope = "playlist-read-private playlist-read-collaborative user-library-read user-read-private"; + static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize"; static const char *spotify_token_uri = "https://accounts.spotify.com/api/token"; + static const char *spotify_playlist_uri = "https://api.spotify.com/v1/playlists/%s"; static const char *spotify_track_uri = "https://api.spotify.com/v1/tracks/%s"; static const char *spotify_me_uri = "https://api.spotify.com/v1/me"; @@ -219,6 +223,13 @@ request_access_tokens(struct keyval *kv, const char **err) spotify_refresh_token = strdup(tmp); } + tmp = jparse_str_from_obj(haystack, "scope"); + if (tmp) + { + free(spotify_granted_scope); + spotify_granted_scope = strdup(tmp); + } + expires_in = jparse_int_from_obj(haystack, "expires_in"); if (expires_in == 0) expires_in = 3600; @@ -914,7 +925,7 @@ spotifywebapi_oauth_uri_get(const char *redirect_uri) ret = ( (keyval_add(&kv, "client_id", spotify_client_id) == 0) && (keyval_add(&kv, "response_type", "code") == 0) && (keyval_add(&kv, "redirect_uri", redirect_uri) == 0) && - (keyval_add(&kv, "scope", "user-read-private playlist-read-private user-library-read") == 0) && + (keyval_add(&kv, "scope", spotify_scope) == 0) && (keyval_add(&kv, "show_dialog", "false") == 0) ); if (!ret) { @@ -1890,11 +1901,19 @@ spotifywebapi_status_info_get(struct spotifywebapi_status_info *info) info->token_valid = token_valid(); if (spotify_user) { - memcpy(info->user, spotify_user, (sizeof(info->user) - 1)); + strncpy(info->user, spotify_user, (sizeof(info->user) - 1)); } if (spotify_user_country) { - memcpy(info->country, spotify_user_country, (sizeof(info->country) - 1)); + strncpy(info->country, spotify_user_country, (sizeof(info->country) - 1)); + } + if (spotify_granted_scope) + { + strncpy(info->granted_scope, spotify_granted_scope, (sizeof(info->granted_scope) - 1)); + } + if (spotify_scope) + { + strncpy(info->required_scope, spotify_scope, (sizeof(info->required_scope) - 1)); } CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck)); @@ -1936,6 +1955,12 @@ spotifywebapi_deinit() CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&token_lck)); spotify_deinit(); + + free(spotify_access_token); + free(spotify_refresh_token); + free(spotify_granted_scope); + free(spotify_user_country); + free(spotify_user); } struct library_source spotifyscanner = diff --git a/src/spotify_webapi.h b/src/spotify_webapi.h index aa62b574..8c8a04e1 100644 --- a/src/spotify_webapi.h +++ b/src/spotify_webapi.h @@ -31,6 +31,8 @@ struct spotifywebapi_status_info bool token_valid; char user[100]; char country[3]; // ISO 3166-1 alpha-2 country code + char granted_scope[250]; + char required_scope[250]; }; struct spotifywebapi_access_token