1

I've been using the HTTR package to make requests to the YouTube Data API. I have two channels that I am getting stats for. Upon the first use, when requesting an access token I did the whole OAuth dance where I was redirected to Google as expected. However, I saved the tokens in .httr-oauth files. I developed a for loop similar to the following for me to get my statistics:

channelOneFile <- ".httr-oauth-channel1"
channelTwoFile <- ".httr-oauth-channel2"

myData <- list()
for(i in 1:2){
    token <- try(suppressWarnings(readRDS(tokenFile)), silent = TRUE)[[1]]
    # And Then I get stats with a GET Request
    url <- paste0("https://youtube.googleapis.com/youtube/v3/videospart=contentDetails%2Csnippet",
                  "&id=",videoId)
    request <- GET(url, token)
    myData[[i]] <- request
}

which worked well for a while. However, after running this code roughly 20x or so (due to some testing and tweaking parameters, ONE of the get requests came back with

Auto-refreshing stale OAuth token.
Warning: Unable to refresh token: invalid_grant
Token has been expired or revoked.

At this point I'd just delete the ".httr-oauth" file, reauthenticate, save the new file, and then my code works again. However, I'm hoping to have this code automated on a local server, where the server (I think) can't do the OAuth dance if the token doesn't work.

When I make a request, I know HTTR automatically uses the refresh token to get a new access token, and I know that Google APIs has a limit of how many times a refresh token can be used. I think that perhaps when it gets the new token it doesn't update the ".httr-oauth" file? How can I get it to do that? If that's not an option, what can I do to do the authentication ONCE, save the credentials and access token and such in a file, and then refer to that file when making server requests?

UPDATE: Here is my OAuth flow:

if (file.exists(tokenFile)) {
   token <- try(suppressWarnings(readRDS(tokenFile)), silent = TRUE)[[1]]
} else if (is.null(appId) | is.null(appSecret)) {
    stop("Missing App Credentials")
} else {
  token <- httr::oauth2.0_token(httr::oauth_endpoints("google"), 
                                httr::oauth_app("google", appId, appSecret), 
                                scope = c("https://www.googleapis.com/auth/youtube.readonly", 
                                                        "https://www.googleapis.com/auth/yt-analytics.readonly"))
}
stvar
  • 6,551
  • 2
  • 13
  • 28
Jacob
  • 406
  • 3
  • 19

1 Answers1

1

You need to refresh your access token, since this kind of OAuth tokens are short lived (usually are valid one hour). Use httrs function refresh_oauth2.0.

See this answer of mine for the procedure of handling refresh tokens offline: How to get my own Google API access token without using "Log in with Google"?

Note that a refresh token can be used as many times as needed for to renew an expiring access token. The refresh tokens usually do not expire (they do when one's app is in testing stage), but can be revoked.

stvar
  • 6,551
  • 2
  • 13
  • 28
  • When I call a GET request, I always get the message `Auto-refreshing stale OAuth token.`, so isn't that refreshing the token already? As for the refresh token thing expiring when in testing stage, both apps are in testing stage (there's one app per channel) and yet one never has this issue and the other does - granted the one that never has the issue is an app that I developed over a year ago, and this one that keeps giving issues is only a month or two old - could that be why? – Jacob Sep 23 '21 at 20:54
  • @Jacob: Older projects (those created before Google introduced the 7 days expiring rule) have automatically their *publishing status* set to *in production*; thus have non-expiring refresh tokens. Note that a project's *publishing status* is displayed within Google's Developers Console. – stvar Sep 23 '21 at 20:59
  • @Jacob: The *stale* message is due to the function [`request_perform`](https://github.com/r-lib/httr/blob/21ff69f219ad11298854a63b8f753389088cf382/R/request.R#L133). Look it up in the [source code of `httr`](https://github.com/r-lib/httr) package: ***`httr` does not implement the proper logic of refreshing access tokens*** (that based on [the expiration timestamp `expires_in`](https://developers.google.com/youtube/v3/live/guides/auth/installed-apps#Store_Tokens) provided by the API along with the tokens themselves). – stvar Sep 23 '21 at 21:23
  • @Jacob: Each time `request_perform` is called (for example upon a `GET` call), ***it refreshes the access token, irrespective of that token being expiring or not***. Consequence: `httr` needs improvements. – stvar Sep 23 '21 at 21:24