Code Monkey home page Code Monkey logo

Comments (11)

jdeboer avatar jdeboer commented on July 26, 2024

I've attempted to solve this by creating a new function oauth2.0_refresh that refreshes an access token using its refresh token. I've based this off the existing oauth2.0_token function from httr and by following the steps demonstrated by R code available from the following links:

  1. https://github.com/greentheo/ROAuthSamples/blob/master/gaGetCredentials_sample.R
  2. http://www.omegahat.org/RGoogleStorage/
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 suggestions

Using the $expires_in value (which is in number of seconds), it would be possible to determine when the access token is due to expire, and with that information automatically refresh it when used if it has expired.

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?

from httr.

hadley avatar hadley commented on July 26, 2024

That looks reasonable. How about adding an has_expired function that determines if a token has expired? Doing it automatically will be harder unless we switch to a ref class for the token object.

The rstudio port problem will be fixed by using httpuv instead of the builtin server.

from httr.

jdeboer avatar jdeboer commented on July 26, 2024

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.

from httr.

jdeboer avatar jdeboer commented on July 26, 2024

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 :)

from httr.

jdeboer avatar jdeboer commented on July 26, 2024

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, oauth2.0_generator, for an oauth reference class. The access token of an object created using this generator should be accessed via the getAccessToken method - that method will refresh and save the token if it has expired, before returning it.

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, incorporates the use of httr's oauth2.0 functions to demonstrate the creation of a new oauth2.0 object using the generator 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)

from httr.

jdeboer avatar jdeboer commented on July 26, 2024

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)

from httr.

hadley avatar hadley commented on July 26, 2024

Could you please file a pull request? It's easier to comment there.

from httr.

hadley avatar hadley commented on July 26, 2024

For your other question, see #32

from httr.

craigcitro avatar craigcitro commented on July 26, 2024

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?

from httr.

jdeboer avatar jdeboer commented on July 26, 2024

@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
Thank you both for for feedback so far, please keep it coming.

from httr.

hadley avatar hadley commented on July 26, 2024

Also in the OAuth branch.

from httr.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.