Skip to content

Commit

Permalink
Merge pull request #3 from thewileylab/oauth_compliance
Browse files Browse the repository at this point in the history
Oauth compliance
  • Loading branch information
the-mayer authored Sep 23, 2022
2 parents f1c2730 + 1519582 commit cb0ad2c
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 64 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: googleGroupR
Title: An R Wrapper for Google Groups Management using the AdminSDK
Version: 0.0.1.9001
Version: 0.0.2.9000
Authors@R: c(
person(given = "David", family = "Mayer", role = c("aut", "cre"), email = "[email protected]"),
person(given = "The Wiley Lab", role = c("cph", "fnd"))
Expand All @@ -12,7 +12,7 @@ BugReports: https://github.com/thewileylab/googleGroupR/issues
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
RoxygenNote: 7.2.1
Depends:
R (>= 3.5.0)
Imports:
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ importFrom(httr,GET)
importFrom(httr,POST)
importFrom(httr,add_headers)
importFrom(httr,content)
importFrom(httr,oauth_listener)
importFrom(jsonlite,fromJSON)
importFrom(magrittr,"%>%")
importFrom(magrittr,extract2)
importFrom(purrr,map_chr)
importFrom(rlang,.data)
importFrom(rlang,format_error_bullets)
importFrom(rlang,inform)
importFrom(tibble,enframe)
124 changes: 73 additions & 51 deletions R/get_access_token.R
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,15 @@ print.hidden_fn <- function(x, ...) {
#' @export
#' @importFrom glue glue
#' @importFrom jsonlite fromJSON
#' @importFrom httr content GET add_headers POST
get_access_token <- function(cached_credentials = '~/.googleGroupR_cache.rds') {
#' @importFrom httr content GET add_headers oauth_listener POST
#' @importFrom rlang format_error_bullets inform
get_access_token <- function(cached_credentials = '~/.config/googleGroupR/.googleGroupR_cache.rds') {
# Cache Directory Setup
cache_dir <- dirname(cached_credentials)
if(dir.exists(cache_dir) == FALSE) {
rlang::inform(rlang::format_error_bullets(c("i" = glue::glue('Creating googleGroupR credential cache at {cache_dir}'))))
dir.create(cache_dir)
}
# OAuth 2.0 Info
## Token URI
token_uri <- "https://oauth2.googleapis.com/token"
Expand All @@ -43,62 +50,77 @@ get_access_token <- function(cached_credentials = '~/.googleGroupR_cache.rds') {
if(file.exists(cached_credentials) ) {
cached_token <- readRDS(cached_credentials)
if ('error' %in% names(cached_token) ) {
message(glue::glue('Please visit the following URL to generate an authorization code: {auth_link}'))
auth_code <- readline(prompt = glue::glue('Enter authorization code:'))
access_token_body <- list(code=auth_code,
rlang::inform(rlang::format_error_bullets(c("x" = 'Error encountered with cached credentials. Obtaining new credentials.')))
auth_code <- oauth_listener(auth_link, is_interactive = interactive())
access_token_body <- list(code=auth_code$code,
client_id=installed_app()$key,
client_secret=installed_app()$secret,
redirect_uri=installed_app()$redirect_uri,
grant_type='authorization_code')
token <- httr::content(httr::POST(url = token_uri, body = access_token_body))
saveRDS(object = token, file = cached_credentials)
token$access_token
invisible(token$access_token)
} else if (!'access_token' %in% names(cached_token)) {
rlang::inform(rlang::format_error_bullets(c("x" = 'Error encountered with cached credentials. Obtaining new credentials.')))
auth_code <- oauth_listener(auth_link, is_interactive = interactive())
access_token_body <- list(code=auth_code$code,
client_id=installed_app()$key,
client_secret=installed_app()$secret,
redirect_uri=installed_app()$redirect_uri,
grant_type='authorization_code')
token <- httr::content(httr::POST(url = token_uri, body = access_token_body))
saveRDS(object = token, file = cached_credentials)
invisible(token$access_token)
} else {
google_oauth_url <- 'https://accounts.google.com/o/oauth2/'
token_info <- httr::content(httr::GET(glue::glue('{google_oauth_url}tokeninfo?access_token={cached_token$access_token}')))
if('expires_in' %in% names(token_info) ){
if(token_info$expires_in > 0 ) {
rlang::inform(rlang::format_error_bullets(c("i" = 'Using access token from cache.')))
cached_token$access_token
} else {
rlang::inform(rlang::format_error_bullets(c("i" = 'Cached token expired, attempting to refresh.')))
# Create Refresh cURL command pieces
refresh_header <- httr::add_headers('Content-Type' = 'application/x-www-form-urlencoded')
refresh_body <- list(grant_type='refresh_token',
refresh_token=cached_token$refresh_token,
client_id=installed_app()$key,
client_secret=installed_app()$secret)
token <- httr::content(httr::POST(url = token_uri, refresh_header, body = refresh_body, encode='form'))
cached_token$access_token <- token$access_token
saveRDS(object = cached_token, file = cached_credentials)
invisible(token$access_token)
}
} else if ('error' %in% names(token_info) ){
rlang::inform(rlang::format_error_bullets(c("i" = 'Cached token expired, obtaining new access token.')))
# Create Refresh cURL command pieces
refresh_header <- httr::add_headers('Content-Type' = 'application/x-www-form-urlencoded')
refresh_body <- list(grant_type='refresh_token',
refresh_token=cached_token$refresh_token,
client_id=installed_app()$key,
client_secret=installed_app()$secret)
token <- httr::content(httr::POST(url = token_uri, refresh_header, body = refresh_body, encode='form'))
cached_token$access_token <- token$access_token
saveRDS(object = cached_token, file = cached_credentials)
invisible(token$access_token)
} else {
message('Not sure how to handle current token situation. Go ask an adult.')
}
}
} else {
google_oauth_url <- 'https://accounts.google.com/o/oauth2/'
token_info <- httr::content(httr::GET(glue::glue('{google_oauth_url}tokeninfo?access_token={cached_token$access_token}')))
if('expires_in' %in% names(token_info) ){
if(token_info$expires_in > 0 ) {
message('Using access token from cache.')
cached_token$access_token
if(Sys.getenv('RSTUDIO_PROGRAM_MODE') == 'desktop') {
auth_code <- oauth_listener(auth_link, is_interactive = interactive())
access_token_body <- list(code=auth_code$code,
client_id=installed_app()$key,
client_secret=installed_app()$secret,
redirect_uri=installed_app()$redirect_uri,
grant_type='authorization_code')
token <- httr::content(httr::POST(url = token_uri, body = access_token_body))
saveRDS(object = token, file = cached_credentials)
invisible(token$access_token)
} else {
message('Cached token expired, obtaining new access token.')
# Create Refresh cURL command pieces
refresh_header <- httr::add_headers('Content-Type' = 'application/x-www-form-urlencoded')
refresh_body <- list(grant_type='refresh_token',
refresh_token=cached_token$refresh_token,
client_id=installed_app()$key,
client_secret=installed_app()$secret)
token <- httr::content(httr::POST(url = token_uri, refresh_header, body = refresh_body, encode='form'))
cached_token$access_token <- token$access_token
saveRDS(object = cached_token, file = cached_credentials)
token$access_token
}
} else if ('error' %in% names(token_info) ){
message('Cached token expired, obtaining new access token.')
# Create Refresh cURL command pieces
refresh_header <- httr::add_headers('Content-Type' = 'application/x-www-form-urlencoded')
refresh_body <- list(grant_type='refresh_token',
refresh_token=cached_token$refresh_token,
client_id=installed_app()$key,
client_secret=installed_app()$secret)
token <- httr::content(httr::POST(url = token_uri, refresh_header, body = refresh_body, encode='form'))
cached_token$access_token <- token$access_token
saveRDS(object = cached_token, file = cached_credentials)
token$access_token
} else{
message('Not sure how to handle current token situation.')
rlang::inform(rlang::format_error_bullets(c("x" = glue::glue("I'm sorry, Google no longer allows Out Of Band (OOB) OAuth 2.0 authentication flows for server environments. \n - Please generate credentials using this package locally and upload to an appropriate location, {cache_dir} by default."),
"i" = 'https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html')))
}
}
}
} else {
message(glue::glue('Please visit the following URL to generate an authorization code: {auth_link}'))
auth_code <- readline(prompt = glue::glue('Enter authorization code:'))
access_token_body <- list(code=auth_code,
client_id=installed_app()$key,
client_secret=installed_app()$secret,
redirect_uri=installed_app()$redirect_uri,
grant_type='authorization_code')
token <- httr::content(httr::POST(url = token_uri, body = access_token_body))
saveRDS(object = token, file = cached_credentials)
token$access_token
}
}
5 changes: 3 additions & 2 deletions R/list_group_members.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
#' @export
#' @importFrom glue glue
#' @importFrom httr add_headers content GET
#' @importFrom rlang format_error_bullets inform
list_group_members <- function (domain, group) {
if (missing(domain) ) {
message('Please specify a GSuite domain.')
rlang::inform(rlang::format_error_bullets(c("x"='Please specify a GSuite domain.')))
} else if (missing(group)) {
message('Please specify a group name.')
rlang::inform(rlang::format_error_bullets(c("x"='Please specify a group name.')))
}else {
group_id <- get_group_id(domain, group)
access_token <- get_access_token()
Expand Down
21 changes: 13 additions & 8 deletions R/list_groups.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@
#' @export
#' @importFrom glue glue
#' @importFrom httr add_headers content GET
#' @importFrom rlang format_error_bullets inform
list_groups <- function(domain) {
if (missing(domain) ) {
message('Please specify a GSuite domain.')
} else {
access_token <- get_access_token()
auth_header <- httr::add_headers('Authorization' = glue::glue('Bearer {access_token}'))
httr::content(httr::GET(glue::glue('https://www.googleapis.com/admin/directory/v1/groups/?domain={domain}'),
auth_header)
)
}
rlang::inform(rlang::format_error_bullets(c("x" = 'Please specify a GSuite domain.')))
} else {
access_token <- get_access_token()
if(is.null(access_token)){
rlang::inform(rlang::format_error_bullets(c("x" = 'Error: Unauthenticated')))
} else {
auth_header <- httr::add_headers('Authorization' = glue::glue('Bearer {access_token}'))
httr::content(httr::GET(glue::glue('https://www.googleapis.com/admin/directory/v1/groups/?domain={domain}'),
auth_header)
)
}
}
}
Binary file modified R/sysdata.rda
Binary file not shown.
Binary file modified inst/secret/client_secret.json
Binary file not shown.
4 changes: 3 additions & 1 deletion man/get_access_token.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cb0ad2c

Please sign in to comment.