2018-05-01 23:24:41 +03:00
|
|
|
#' @export
|
|
|
|
AzureToken <- R6::R6Class("AzureToken", inherit=httr::Token2.0,
|
|
|
|
|
|
|
|
public=list(
|
2018-05-02 23:01:58 +03:00
|
|
|
|
2018-05-01 23:24:41 +03:00
|
|
|
# need to do hacky init to support explicit re-authentication instead of using a refresh token
|
2018-05-02 23:01:58 +03:00
|
|
|
initialize=function(endpoint, app, user_params, use_device=FALSE)
|
2018-05-01 23:24:41 +03:00
|
|
|
{
|
2018-05-02 23:01:58 +03:00
|
|
|
private$az_use_device <- use_device
|
2018-05-01 23:24:41 +03:00
|
|
|
|
|
|
|
params <- list(scope=NULL, user_params=user_params, type=NULL, use_oob=FALSE, as_header=TRUE,
|
2018-05-03 03:39:18 +03:00
|
|
|
use_basic_auth=use_device, config_init=list(), client_credentials=TRUE)
|
2018-05-01 23:24:41 +03:00
|
|
|
|
|
|
|
super$initialize(app=app, endpoint=endpoint, params=params, credentials=NULL, cache_path=FALSE)
|
2018-05-02 23:01:58 +03:00
|
|
|
|
|
|
|
# if auth is via device, token now contains initial server response; call devicecode handler to get actual token
|
|
|
|
if(use_device)
|
|
|
|
private$init_with_device(endpoint, app, user_params)
|
|
|
|
},
|
|
|
|
|
|
|
|
# overrides httr::Token2.0 method
|
|
|
|
can_refresh=function()
|
|
|
|
{
|
|
|
|
TRUE # always can refresh
|
2018-05-01 23:24:41 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
# overrides httr::Token2.0 method
|
|
|
|
validate=function()
|
|
|
|
{
|
2018-05-03 05:10:10 +03:00
|
|
|
if(!is.null(self$endpoint$validate))
|
2018-05-01 23:24:41 +03:00
|
|
|
return(super$validate())
|
|
|
|
|
|
|
|
expdate <- as.POSIXct(as.numeric(self$credentials$expires_on), origin="1970-01-01")
|
|
|
|
curdate <- Sys.time()
|
|
|
|
curdate < expdate
|
|
|
|
},
|
|
|
|
|
|
|
|
# overrides httr::Token2.0 method
|
|
|
|
refresh=function()
|
|
|
|
{
|
|
|
|
if(!is.null(self$credentials$refresh_token))
|
|
|
|
return(super$refresh())
|
|
|
|
|
|
|
|
# re-authenticate if no refresh token
|
2018-05-02 23:01:58 +03:00
|
|
|
self$initialize(self$endpoint, self$app, self$params$user_params, use_device=private$az_use_device)
|
2018-05-01 23:24:41 +03:00
|
|
|
NULL
|
|
|
|
}
|
|
|
|
),
|
|
|
|
|
|
|
|
private=list(
|
2018-05-02 23:01:58 +03:00
|
|
|
az_use_device=NULL,
|
|
|
|
|
|
|
|
# device code authentication: after sending initial request, loop until server indicates code has been received
|
|
|
|
# after init_oauth2.0, oauth2.0_access_token
|
|
|
|
init_with_device=function(endpoint, app, user_params)
|
|
|
|
{
|
2018-05-02 23:16:09 +03:00
|
|
|
cat(self$credentials$message, "\n") # tell user to enter the code
|
|
|
|
|
2018-05-02 23:01:58 +03:00
|
|
|
req_params <- list(client_id=app$key, grant_type="device_code", code=self$credentials$device_code)
|
|
|
|
req_params <- utils::modifyList(user_params, req_params)
|
2018-05-03 03:39:18 +03:00
|
|
|
endpoint$access <- sub("devicecode", "token", endpoint$access)
|
2018-05-02 23:47:15 +03:00
|
|
|
|
2018-05-02 23:01:58 +03:00
|
|
|
interval <- as.numeric(self$credentials$interval)
|
2018-05-02 23:16:09 +03:00
|
|
|
ntries <- as.numeric(self$credentials$expires_in) %/% interval
|
|
|
|
for(i in seq_len(ntries))
|
2018-05-02 23:01:58 +03:00
|
|
|
{
|
|
|
|
Sys.sleep(interval)
|
|
|
|
|
2018-05-03 03:39:18 +03:00
|
|
|
res <- httr::POST(endpoint$access, httr::add_headers(`Cache-Control`="no-cache"), encode="form",
|
|
|
|
body=req_params)
|
2018-05-02 23:01:58 +03:00
|
|
|
|
|
|
|
status <- httr::status_code(res)
|
2018-05-02 23:47:15 +03:00
|
|
|
cont <- httr::content(res)
|
|
|
|
if(status == 400 && cont$error == "authorization_pending")
|
2018-05-02 23:01:58 +03:00
|
|
|
{
|
2018-05-02 23:47:15 +03:00
|
|
|
msg <- sub("[\r\n].*", "", cont$error_description)
|
2018-05-02 23:01:58 +03:00
|
|
|
cat(msg, "\n")
|
|
|
|
}
|
|
|
|
else if(status >= 300)
|
|
|
|
httr::stop_for_status(res)
|
|
|
|
else break
|
|
|
|
}
|
|
|
|
if(status >= 300)
|
|
|
|
stop("Unable to authenticate")
|
|
|
|
|
|
|
|
# replace original fields with authenticated fields
|
|
|
|
self$endpoint <- endpoint
|
2018-05-02 23:47:15 +03:00
|
|
|
self$credentials <- cont
|
2018-05-02 23:01:58 +03:00
|
|
|
NULL
|
|
|
|
}
|
2018-05-01 23:24:41 +03:00
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
#' @export
|
2018-05-09 09:02:38 +03:00
|
|
|
get_azure_token=function(aad_host, tenant, app, auth_type=c("client_credentials", "device_code"), secret, arm_host)
|
2018-05-01 23:24:41 +03:00
|
|
|
{
|
2018-05-07 05:33:29 +03:00
|
|
|
auth_type <- match.arg(auth_type)
|
2018-05-01 23:24:41 +03:00
|
|
|
base_url <- file.path(aad_host, tenant, fsep="/")
|
2018-05-09 09:02:38 +03:00
|
|
|
if(auth_type == "client_credentials")
|
2018-05-01 23:24:41 +03:00
|
|
|
auth_with_creds(base_url, app, secret, arm_host)
|
|
|
|
else auth_with_device(base_url, app, arm_host)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auth_with_creds <- function(base_url, app, secret, resource)
|
|
|
|
{
|
|
|
|
endp <- httr::oauth_endpoint(base_url=base_url, authorize="oauth2/authorize", access="oauth2/token")
|
|
|
|
app <- httr::oauth_app("azure", key=app, secret=secret)
|
|
|
|
|
2018-05-02 23:01:58 +03:00
|
|
|
AzureToken$new(endp, app, user_params=list(resource=resource))
|
2018-05-01 23:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auth_with_device <- function(base_url, app, resource)
|
|
|
|
{
|
|
|
|
endp <- httr::oauth_endpoint(base_url=base_url, authorize="oauth2/authorize", access="oauth2/devicecode")
|
2018-05-03 03:39:18 +03:00
|
|
|
app <- httr::oauth_app("azure", key=app, secret=NULL)
|
2018-05-01 23:24:41 +03:00
|
|
|
|
2018-05-02 23:01:58 +03:00
|
|
|
AzureToken$new(endp, app, user_params=list(resource=resource), use_device=TRUE)
|
2018-05-01 23:24:41 +03:00
|
|
|
}
|
2018-05-09 09:02:38 +03:00
|
|
|
|
|
|
|
|
|
|
|
is_token <- function(object)
|
|
|
|
{
|
|
|
|
R6::is.R6(object) && inherits(object, "AzureToken")
|
|
|
|
}
|