This app shows a user’s top tracks after they authorize access to Spotify. Spotify handles sign-in and returns an access token, a credential your app can use to ask its API for data. The app never needs the user’s Spotify password.
Scopes name the permissions to request. Here
user-top-read allows reading listening preferences; other
data needs different permissions. For the shared login setup, see Usage.
http://127.0.0.1:8100 as its redirect URI.
Spotify accepts a loopback IP address for local HTTP development, but
not localhost. See Spotify’s
redirect URI rules.SPOTIFY_OAUTH_CLIENT_ID and
SPOTIFY_OAUTH_CLIENT_SECRET in your R environment. See the
environment setup
example for .Renviron syntax.Save this as app.R, run it, and open
http://127.0.0.1:8100 in a regular browser. After
connecting Spotify, click Load top tracks.
library(shiny)
library(shinyOAuth)
client <- oauth_client(
provider = oauth_provider_spotify(),
client_id = Sys.getenv("SPOTIFY_OAUTH_CLIENT_ID"),
client_secret = Sys.getenv("SPOTIFY_OAUTH_CLIENT_SECRET"),
redirect_uri = "http://127.0.0.1:8100",
scopes = c("user-read-private", "user-top-read"),
resource_bases = c(spotify = "https://api.spotify.com/v1")
)
ui <- oauth_ui(fluidPage(
h2("My top tracks"),
actionButton("load", "Load top tracks"),
tableOutput("tracks")
), id = "auth", client = client)
server <- function(input, output, session) {
auth <- oauth_module_server("auth", client)
tracks <- reactiveVal(NULL)
observe({
connection <- auth[["connection"]]()
result <- isolate(tracks())
if (is.null(connection) ||
!connection[["has_scopes"]]("user-top-read") ||
(!is.null(result) &&
!identical(result[["authorization"]], connection[["id"]]))) {
tracks(NULL)
}
})
observeEvent(input[["load"]], {
connection <- req(auth[["connection"]]())
req(connection[["has_scopes"]]("user-top-read"))
data <- tryCatch({
response <- connection[["request"]](
"spotify", "me/top/tracks",
query = list(limit = 10, time_range = "short_term"),
required_scopes = "user-top-read",
refresh = TRUE
)
httr2::resp_check_status(response)
body <- httr2::resp_body_json(response, simplifyVector = FALSE)
vapply(body[["items"]], function(track) track[["name"]], character(1))
}, error = function(e) NULL)
tracks(list(authorization = connection[["id"]], data = data))
})
output[["tracks"]] <- renderTable({
connection <- req(auth[["connection"]]())
req(connection[["has_scopes"]]("user-top-read"))
result <- tracks()
req(identical(result[["authorization"]], connection[["id"]]))
values <- result[["data"]]
validate(need(!is.null(values), "Could not load tracks. Try again later."))
validate(need(length(values) > 0, "No listening data to show yet."))
data.frame(Track = values)
})
}
runApp(shinyApp(ui, server), port = 8100, launch.browser = FALSE)The request asks for up to ten tracks from the short-term range,
using the user-top-read permission. The module obtains and
stores the access token; the connection checks the operation’s scope and
approved API address, refreshing the token if needed before the request.
observeEvent() loads the data when the button is clicked,
and renderTable() displays it for the current login. The
observer clears cached tracks on logout, a new login, or loss of
permission to read them.
Other Spotify endpoints require their corresponding scopes. Empty results can be normal for accounts without listening history; API errors can indicate missing permissions, app access restrictions, or request limits.
A larger example is installed with the package. It adds profile information, currently playing music, top artists, and recent tracks, with a bslib layout. It uses the same authentication setup as the example above.
install.packages(c("bslib", "ggplot2", "DT", "purrr", "dplyr"))
dashboard_file <- system.file(
"examples", "spotify-dashboard.R", package = "shinyOAuth", mustWork = TRUE
)
file.show(dashboard_file) # Read or copy the complete source.
source(dashboard_file) # Run after setting your Spotify credentials.The installed dashboard validates Spotify link and image URLs and escapes text in its HTML output.