-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refreshing expired token with OAuth 2.0 authentication #31
Comments
I've attempted to solve this by creating a new function
oauth2.0_refresh <- function(endpoint, app, access_token, type = NULL) {
req <- POST(
url = endpoint$access,
multipart = FALSE,
body = list(
client_id = app$key,
client_secret = app$secret,
grant_type = "refresh_token",
refresh_token = access_token$refresh_token
)
)
content_out <- content(req, type = type)
content_out <- c(content_out, access_token['refresh_token'])
} I can successfully refresh an access token using the above function. Further suggestionsUsing the Also, is there a way to configure httr to not use the callback method in order to support RStudio, by instead requiring users to manually copy and paste the access code from their browser to the RStudio console? |
That looks reasonable. How about adding an The rstudio port problem will be fixed by using httpuv instead of the builtin server. |
Yes, to do that I think the token list object would need an expiration date-time field added. The function would simply compare it to the date and time as of when the has_expired function is called. |
Hi Hadley, I forked httr and made some amendments to handle the refreshing of OAuth 2.0 access tokens with httr. Would you mind please taking a look? I would also like to ask for some guidance around the rstudio port problem that you mentioned that will be fixed by using httpuv instead of the built-in server. Is there work already started on this? If not, I would like to try to figure this one out if I can - will probably need some help though :) |
I've written the following R code that creates a reference class for automatically refreshing an expired oauth2.0 token created by httr. It also handles saving the token to the file system. Please let me know if you have any feedback on the code as this is my first attempt at creating reference classes. Feedback on exception handling would be particularly useful. First, the below R code depends on the following forked version of httr which has functions for checking and refreshing expired access tokens - the forked version can be installed as follows: library(devtools)
install_github(repo = "httr", username = "jdeboer") The following code then creates a generator function, library(httr)
setClassUnion(
name = "characterOrNull",
members = c("character", "NULL")
)
oauth2.0_generator <- setRefClass(
Class = "oauth2.0",
fields = list(
file = "character",
endpoint = "list",
app = "list",
scope = "character",
type = "characterOrNull",
access_token = "list",
margin = "numeric"
),
methods = list(
initialize = function(file, endpoint, app, scope, type, margin = 5) {
.self$file <- file
.self$endpoint <- endpoint
.self$app <- app
.self$scope <- scope
.self$type <- type
.self$margin <- margin
if(file.exists(file)) {
.self$access_token <- readRDS(file)
.self$access_token <- .self$getAccessToken()
} else {
.self$access_token <- oauth2.0_token(
endpoint = endpoint,
app = app,
scope = scope_url,
type = type
)
saveRDS(access_token, file = file)
}
return(.self)
},
getAccessToken = function() {
if (
oauth2.0_has_expired(
access_token = access_token,
margin = margin
)
) {
.self$access_token <- oauth2.0_refresh(
endpoint = endpoint,
app = app,
access_token = access_token,
type = type
)
saveRDS(access_token, file = file)
}
return(access_token)
}
)
) The following function, new_oauth <- function(
token_file,
base_url,
authorize_url,
access_url,
scope_url,
appname,
client_id,
client_secret = NULL,
type = NULL
) {
endpoint <- oauth_endpoint(
request = NULL,
authorize = authorize_url,
access = access_url,
base_url = base_url
)
app <- oauth_app(
appname = appname,
key = client_id,
secret = client_secret
)
oauth <- oauth2.0_generator$new(
endpoint = endpoint,
app = app,
scope = scope_url,
file = token_file,
type = type
)
return(oauth)
} A new oauth object can then be created to make a request to, for example, Google Analytics: authorize_url <- "auth"
access_url <- "token"
base_url <- "https://accounts.google.com/o/oauth2"
appname <- "GANALYTICS"
client_id <- "123456789012.apps.googleusercontent.com"
scope_url <- "https://www.googleapis.com/auth/analytics.readonly"
token_file <- "~/ganalytics_token.RDS"
oauth <- new_oauth(
token_file = token_file,
base_url = base_url,
authorize_url = authorize_url,
access_url = access_url,
scope_url = scope_url,
appname = appname,
client_id = client_id
) The access_token string from the oauth object can be accessed and used to make the request as demonstrated below: request.config <- sign_oauth2.0(
access_token = oauth$access_token$access_token
)
query.url <- "https://www.googleapis.com/analytics/v3/data/ga?ids=ga%3A12345678&dimensions=ga%3Adate&metrics=ga%3Avisits&start-date=2013-04-21&end-date=2013-05-05&max-results=50"
response <- GET(
url = query.url,
request.config
)
print(response) |
To utilise the automatic refreshing of the access token, the last code snippet above should be replaced with: query.url <- "https://www.googleapis.com/analytics/v3/data/ga?ids=ga%3A12345678&dimensions=ga%3Adate&metrics=ga%3Avisits&start-date=2013-04-21&end-date=2013-05-05&max-results=50"
response <- GET(
url = query.url,
config = sign_oauth2.0(
access_token = oauth$getAccessToken()$access_token
)
)
print(response) |
Could you please file a pull request? It's easier to comment there. |
For your other question, see #32 |
There must be OAuth2 in the air -- I've actually been implementing some similar functionality in the last week. I'm interested to see this pull request, but I had one high-level question: I like the idea of using a reference class to handle the update of the credential behind the scenes, but I don't know that I'd want just the credential to be the mutable object. It feels like you want some sort of "connection" or "session" object (not an R connection, just a poor coincidence of naming) representing an "authorized HTTP instance", since that's what the user will ultimately want to use. For instance, what you'll probably want to do in code using this library is to create a new authenticated connection object (either loading the credential from disk or re-doing the auth dance), and then make a series of requests. There's a handful of related data (endpoint info, API info, app info, etc) that needs to be threaded through -- it seems nice to package that all together, and have that top-level object be mutable and contain immutable pieces. Or am I overthinking it? |
@craigcitro and @hadley Please check out the following pull request from a new branch within my httr repo regarding the use of a reference class for handling OAuth2.0 sessions. jdeboer#1 |
Also in the OAuth branch. |
How can the authentication token be refreshed using the refresh_token instead of the user needing to grant access again? Is it possible for httr to automatically refresh using the refresh_token when the access_token ised but has expired?
Problem described as follows:
With the OAuth 2.0 client secret for my app held in an environment variable named "GANALYTICS_CONSUMER_SECRET"
Note: The
client_id
below has been replaced with a dummy value.Using Windows 7 64-bit and R version 3.0.0, the below code in the R GUI will successfully request access from the user to Google Analytics, then pass the resulting authentication token back to R via a redirect to the localhost. (Note this does not currently work in RStudio.)
Passing
access_token
tosign_oauth2.0
results in the a malformed configuration query if passed toGET
, i.e. the list object is flattened to a string starting as follows"access_token=list%28access%5Ftoken%20%3D%20%22
and ending%22%29"
However, passing
access_token$access_token
tosign_oauth2.0
works.Therefore the following is executed to create the configure and send the request to Google Analytics (note the Google Analytics profile ID has been replaced with a dummy value):
The above successfully retrieves the requested data from Google Analytics.
However, the token expires after 3600 seconds and needs to be refreshed using the
refresh_token
item from theaccess_token
list object:Note: the
$access_token
and$refresh_token
have been masked from view.Once the token has expired, if used as above, it results in Google Analytics returning a 401 HTTP status code with the following authentication error message "Login Required"
How can the authentication token be refreshed using the refresh_token instead of the user needing to grant access again? Is it possible for httr to automatically refresh using the refresh_token when the access_token ised but has expired?
Thank you.
The text was updated successfully, but these errors were encountered: