зеркало из https://github.com/Azure/AzureAuth.git
Merge branch 'master' of https://github.com/cloudyr/AzureAuth
This commit is contained in:
Коммит
ba250fe15f
|
@ -2,11 +2,15 @@
|
|||
|
||||
export(AzureR_dir)
|
||||
export(AzureToken)
|
||||
export(AzureTokenV1)
|
||||
export(AzureTokenV2)
|
||||
export(clean_token_directory)
|
||||
export(delete_azure_token)
|
||||
export(format_auth_header)
|
||||
export(get_azure_token)
|
||||
export(is_azure_token)
|
||||
export(is_azure_v1_token)
|
||||
export(is_azure_v2_token)
|
||||
export(is_guid)
|
||||
export(list_azure_tokens)
|
||||
export(normalize_guid)
|
||||
|
|
103
R/AzureToken.R
103
R/AzureToken.R
|
@ -1,39 +1,39 @@
|
|||
#' Azure OAuth authentication
|
||||
#'
|
||||
#' Azure OAuth 2.0 token class, with an interface based on the [Token2.0 class][httr::Token2.0] in httr. Rather than calling the initialization method directly, tokens should be created via [get_azure_token()].
|
||||
#' Azure OAuth 2.0 token classes, with an interface based on the [Token2.0 class][httr::Token2.0] in httr. Rather than calling the initialization methods directly, tokens should be created via [get_azure_token()].
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Methods:
|
||||
#' - `refresh`: Refreshes the token. For expired Azure tokens using client credentials, refreshing really means requesting a new token.
|
||||
#' - `validate`: Checks if the token is still valid. For Azure tokens using client credentials, this just checks if the current time is less than the token's expiry time.
|
||||
#' - `hash`: Computes an MD5 hash on selected fields of the token. Used internally for identification purposes when caching.
|
||||
#' - `refresh`: Refreshes the token. For expired tokens without an associated refresh token, refreshing really means requesting a new token.
|
||||
#' - `validate`: Checks if the token is still valid. If there is no associated refresh token, this just checks if the current time is less than the token's expiry time.
|
||||
#' - `hash`: Computes an MD5 hash on the input fields of the object. Used internally for identification purposes when caching.
|
||||
#' - `cache`: Stores the token on disk for use in future sessions.
|
||||
#'
|
||||
#' @seealso
|
||||
#' [get_azure_token], [httr::Token]
|
||||
#'
|
||||
#' @format An R6 object of class `AzureToken`.
|
||||
#' @format An R6 object representing an Azure Active Directory token and its associated credentials. The `AzureTokenV1` class is for AAD v1.0 tokens, and the `AzureTokenV2` class is for AAD v2.0 tokens. Objects of the AzureToken class should not be created directly.
|
||||
#' @export
|
||||
AzureToken <- R6::R6Class("AzureToken",
|
||||
|
||||
public=list(
|
||||
|
||||
version=NULL,
|
||||
aad_host=NULL,
|
||||
tenant=NULL,
|
||||
auth_type=NULL,
|
||||
client=NULL,
|
||||
resource=NULL,
|
||||
scope=NULL,
|
||||
authorize_args=NULL,
|
||||
token_args=NULL,
|
||||
credentials=list(), # returned token details from host
|
||||
|
||||
initialize=function(resource, tenant, app, password=NULL, username=NULL, certificate=NULL, auth_type=NULL,
|
||||
aad_host="https://login.microsoftonline.com/", version=1,
|
||||
initialize=function(tenant, app, password=NULL, username=NULL, certificate=NULL, auth_type=NULL,
|
||||
aad_host="https://login.microsoftonline.com/",
|
||||
authorize_args=list(), token_args=list())
|
||||
{
|
||||
self$version <- normalize_aad_version(version)
|
||||
# fail if this constructor is called directly
|
||||
if(is.null(self$version))
|
||||
stop("Do not call this constructor directly; use get_azure_token() instead")
|
||||
|
||||
self$aad_host <- aad_host
|
||||
self$tenant <- normalize_tenant(tenant)
|
||||
self$auth_type <- select_auth_type(password, username, certificate, auth_type)
|
||||
|
@ -43,10 +43,7 @@ public=list(
|
|||
self$authorize_args <- authorize_args
|
||||
self$token_args <- token_args
|
||||
|
||||
if(self$version == 1)
|
||||
self$resource <- resource
|
||||
else self$scope <- sapply(resource, verify_v2_scope, USE.NAMES=FALSE)
|
||||
|
||||
# set the "real" init method based on auth type
|
||||
private$initfunc <- switch(self$auth_type,
|
||||
authorization_code=init_authcode,
|
||||
device_code=init_devcode,
|
||||
|
@ -122,7 +119,7 @@ public=list(
|
|||
list(grant_type="refresh_token", refresh_token=self$credentials$refresh_token))
|
||||
body <- private$build_access_body(body)
|
||||
|
||||
uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "token")
|
||||
uri <- private$aad_endpoint("token")
|
||||
httr::POST(uri, body=body, encode="form")
|
||||
}
|
||||
else private$initfunc() # reauthenticate if no refresh token
|
||||
|
@ -157,16 +154,74 @@ private=list(
|
|||
self$credentials <- token$credentials
|
||||
},
|
||||
|
||||
build_access_body=function(body=self$client)
|
||||
{
|
||||
stopifnot(is.list(self$token_args))
|
||||
body <- if(self$version == 1)
|
||||
c(body, self$authorize_args, resource=self$resource)
|
||||
else c(body, self$authorize_args, scope=paste(self$scope, collapse=" "))
|
||||
},
|
||||
|
||||
# member function to be filled in by initialize()
|
||||
initfunc=NULL
|
||||
))
|
||||
|
||||
|
||||
#' @rdname AzureToken
|
||||
#' @export
|
||||
AzureTokenV1 <- R6::R6Class("AzureTokenV1", inherit=AzureToken,
|
||||
|
||||
public=list(
|
||||
|
||||
version=1, # for compatibility
|
||||
resource=NULL,
|
||||
|
||||
initialize=function(resource, ...)
|
||||
{
|
||||
self$resource <- resource
|
||||
super$initialize(...)
|
||||
}
|
||||
),
|
||||
|
||||
private=list(
|
||||
|
||||
build_access_body=function(body=self$client)
|
||||
{
|
||||
stopifnot(is.list(self$token_args))
|
||||
c(body, self$authorize_args, resource=self$resource)
|
||||
},
|
||||
|
||||
aad_endpoint=function(type)
|
||||
{
|
||||
uri <- httr::parse_url(self$aad_host)
|
||||
uri$path <- file.path(self$tenant, "oauth2", type)
|
||||
httr::build_url(uri)
|
||||
}
|
||||
|
||||
))
|
||||
|
||||
|
||||
#' @rdname AzureToken
|
||||
#' @export
|
||||
AzureTokenV2 <- R6::R6Class("AzureTokenV2", inherit=AzureToken,
|
||||
|
||||
public=list(
|
||||
|
||||
version=2, # for compatibility
|
||||
scope=NULL,
|
||||
|
||||
initialize=function(resource, ...)
|
||||
{
|
||||
self$scope <- sapply(resource, verify_v2_scope, USE.NAMES=FALSE)
|
||||
super$initialize(...)
|
||||
}
|
||||
),
|
||||
|
||||
private=list(
|
||||
|
||||
build_access_body=function(body=self$client)
|
||||
{
|
||||
stopifnot(is.list(self$token_args))
|
||||
c(body, self$authorize_args, scope=paste(self$scope, collapse=" "))
|
||||
},
|
||||
|
||||
aad_endpoint=function(type)
|
||||
{
|
||||
uri <- httr::parse_url(self$aad_host)
|
||||
uri$path <- file.path(self$tenant, "oauth2/v2.0", type)
|
||||
httr::build_url(uri)
|
||||
}
|
||||
))
|
||||
|
||||
|
|
15
R/format.R
15
R/format.R
|
@ -10,11 +10,16 @@ format_auth_header <- function(token)
|
|||
expiry <- as.POSIXct(as.numeric(token$credentials$expires_on), origin="1970-01-01")
|
||||
obtained <- expiry - as.numeric(token$credentials$expires_in)
|
||||
|
||||
res <- if(token$version == 1)
|
||||
paste("resource", token$resource)
|
||||
else paste("scope", paste(token$scope, collapse=" "))
|
||||
|
||||
version <- if(token$version == 1) "v1.0" else "v2.0"
|
||||
if(is_azure_v1_token(token))
|
||||
{
|
||||
version <- "v1.0"
|
||||
res <- paste("resource", token$resource)
|
||||
}
|
||||
else
|
||||
{
|
||||
version <- "v2.0"
|
||||
res <- paste("scope", paste(token$scope, collapse=" "))
|
||||
}
|
||||
|
||||
tenant <- token$tenant
|
||||
if(tenant == "common")
|
||||
|
|
|
@ -4,7 +4,7 @@ init_authcode <- function()
|
|||
stop("httpuv package must be installed to use authorization_code method", call.=FALSE)
|
||||
|
||||
# browse to authorization endpoint to get code
|
||||
auth_uri <- httr::parse_url(aad_endpoint(self$aad_host, self$tenant, self$version, "authorize") )
|
||||
auth_uri <- httr::parse_url(private$aad_endpoint("authorize"))
|
||||
|
||||
opts <- utils::modifyList(list(
|
||||
client_id=self$client$client_id,
|
||||
|
@ -23,7 +23,7 @@ init_authcode <- function()
|
|||
code <- listen_for_authcode(auth_uri, host, redirect$port)
|
||||
|
||||
# contact token endpoint for token
|
||||
access_uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "token")
|
||||
access_uri <- private$aad_endpoint("token")
|
||||
body <- c(self$client, code=code, redirect_uri=opts$redirect_uri)
|
||||
|
||||
httr::POST(access_uri, body=body, encode="form")
|
||||
|
@ -33,7 +33,7 @@ init_authcode <- function()
|
|||
init_devcode <- function()
|
||||
{
|
||||
# contact devicecode endpoint to get code
|
||||
dev_uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "devicecode")
|
||||
dev_uri <- private$aad_endpoint("devicecode")
|
||||
body <- private$build_access_body(list(client_id=self$client$client_id))
|
||||
|
||||
res <- httr::POST(dev_uri, body=body, encode="form")
|
||||
|
@ -43,7 +43,7 @@ init_devcode <- function()
|
|||
cat(creds$message, "\n")
|
||||
|
||||
# poll token endpoint for token
|
||||
access_uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "token")
|
||||
access_uri <- private$aad_endpoint("token")
|
||||
body <- c(self$client, code=creds$device_code)
|
||||
|
||||
poll_for_token(access_uri, body, creds$interval, creds$expires_in)
|
||||
|
@ -53,7 +53,7 @@ init_devcode <- function()
|
|||
init_clientcred <- function()
|
||||
{
|
||||
# contact token endpoint directly with client credentials
|
||||
uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "token")
|
||||
uri <- private$aad_endpoint("token")
|
||||
body <- private$build_access_body()
|
||||
|
||||
httr::POST(uri, body=body, encode="form")
|
||||
|
@ -63,7 +63,7 @@ init_clientcred <- function()
|
|||
init_resowner <- function()
|
||||
{
|
||||
# contact token endpoint directly with resource owner username/password
|
||||
uri <- aad_endpoint(self$aad_host, self$tenant, self$version, "token")
|
||||
uri <- private$aad_endpoint("token")
|
||||
body <- private$build_access_body()
|
||||
|
||||
httr::POST(uri, body=body, encode="form")
|
||||
|
|
31
R/token.R
31
R/token.R
|
@ -50,7 +50,7 @@
|
|||
#' To delete _all_ cached tokens, use `clean_token_directory`.
|
||||
#'
|
||||
#' @section Value:
|
||||
#' For `get_azure_token`, an object of class `AzureToken` representing the AAD token. For `list_azure_tokens`, a list of such objects retrieved from disk.
|
||||
#' For `get_azure_token`, an object of class either `AzureTokenV1` or `AzureTokenV2` depending on whether the token is for AAD v1.0 or v2.0. For `list_azure_tokens`, a list of such objects retrieved from disk.
|
||||
#'
|
||||
#' @seealso
|
||||
#' [AzureToken], [httr::oauth2.0_token], [httr::Token],
|
||||
|
@ -109,8 +109,11 @@ get_azure_token <- function(resource, tenant, app, password=NULL, username=NULL,
|
|||
aad_host="https://login.microsoftonline.com/", version=1,
|
||||
authorize_args=list(), token_args=list())
|
||||
{
|
||||
AzureToken$new(resource, tenant, app, password, username, certificate, auth_type, aad_host, version,
|
||||
authorize_args, token_args)
|
||||
if(normalize_aad_version(version) == 1)
|
||||
AzureTokenV1$new(resource, tenant, app, password, username, certificate, auth_type, aad_host,
|
||||
authorize_args, token_args)
|
||||
else AzureTokenV2$new(resource, tenant, app, password, username, certificate, auth_type, aad_host,
|
||||
authorize_args, token_args)
|
||||
}
|
||||
|
||||
|
||||
|
@ -247,7 +250,13 @@ construct_path <- function(...)
|
|||
}
|
||||
|
||||
|
||||
#' @param object For `is_azure_token`, an R object.
|
||||
is_empty <- function(x)
|
||||
{
|
||||
is.null(x) || length(x) == 0
|
||||
}
|
||||
|
||||
|
||||
#' @param object For `is_azure_token`, `is_azure_v1_token` and `is_azure_v2_token`, an R object.
|
||||
#' @rdname get_azure_token
|
||||
#' @export
|
||||
is_azure_token <- function(object)
|
||||
|
@ -256,7 +265,17 @@ is_azure_token <- function(object)
|
|||
}
|
||||
|
||||
|
||||
is_empty <- function(x)
|
||||
#' @rdname get_azure_token
|
||||
#' @export
|
||||
is_azure_v1_token <- function(object)
|
||||
{
|
||||
is.null(x) || length(x) == 0
|
||||
is_azure_token(object) && inherits(object, "AzureTokenV1")
|
||||
}
|
||||
|
||||
|
||||
#' @rdname get_azure_token
|
||||
#' @export
|
||||
is_azure_v2_token <- function(object)
|
||||
{
|
||||
is_azure_token(object) && inherits(object, "AzureTokenV2")
|
||||
}
|
||||
|
|
14
R/utils.R
14
R/utils.R
|
@ -35,20 +35,6 @@ aad_request_credentials <- function(app, password, username, certificate, auth_t
|
|||
}
|
||||
|
||||
|
||||
aad_endpoint <- function(aad_host, tenant, version=1, type=c("authorize", "token", "devicecode"))
|
||||
{
|
||||
type <- match.arg(type)
|
||||
tenant <- normalize_tenant(tenant)
|
||||
|
||||
uri <- httr::parse_url(aad_host)
|
||||
uri$path <- if(version == 1)
|
||||
file.path(tenant, "oauth2", type)
|
||||
else file.path(tenant, "oauth2/v2.0", type)
|
||||
|
||||
httr::build_url(uri)
|
||||
}
|
||||
|
||||
|
||||
normalize_aad_version <- function(v)
|
||||
{
|
||||
if(v == "v1.0")
|
||||
|
|
|
@ -3,20 +3,26 @@
|
|||
\docType{class}
|
||||
\name{AzureToken}
|
||||
\alias{AzureToken}
|
||||
\alias{AzureTokenV1}
|
||||
\alias{AzureTokenV2}
|
||||
\title{Azure OAuth authentication}
|
||||
\format{An R6 object of class \code{AzureToken}.}
|
||||
\format{An R6 object representing an Azure Active Directory token and its associated credentials. The \code{AzureTokenV1} class is for AAD v1.0 tokens, and the \code{AzureTokenV2} class is for AAD v2.0 tokens. Objects of the AzureToken class should not be created directly.}
|
||||
\usage{
|
||||
AzureToken
|
||||
|
||||
AzureTokenV1
|
||||
|
||||
AzureTokenV2
|
||||
}
|
||||
\description{
|
||||
Azure OAuth 2.0 token class, with an interface based on the \link[httr:Token2.0]{Token2.0 class} in httr. Rather than calling the initialization method directly, tokens should be created via \code{\link[=get_azure_token]{get_azure_token()}}.
|
||||
Azure OAuth 2.0 token classes, with an interface based on the \link[httr:Token2.0]{Token2.0 class} in httr. Rather than calling the initialization methods directly, tokens should be created via \code{\link[=get_azure_token]{get_azure_token()}}.
|
||||
}
|
||||
\section{Methods}{
|
||||
|
||||
\itemize{
|
||||
\item \code{refresh}: Refreshes the token. For expired Azure tokens using client credentials, refreshing really means requesting a new token.
|
||||
\item \code{validate}: Checks if the token is still valid. For Azure tokens using client credentials, this just checks if the current time is less than the token's expiry time.
|
||||
\item \code{hash}: Computes an MD5 hash on selected fields of the token. Used internally for identification purposes when caching.
|
||||
\item \code{refresh}: Refreshes the token. For expired tokens without an associated refresh token, refreshing really means requesting a new token.
|
||||
\item \code{validate}: Checks if the token is still valid. If there is no associated refresh token, this just checks if the current time is less than the token's expiry time.
|
||||
\item \code{hash}: Computes an MD5 hash on the input fields of the object. Used internally for identification purposes when caching.
|
||||
\item \code{cache}: Stores the token on disk for use in future sessions.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
\alias{list_azure_tokens}
|
||||
\alias{token_hash}
|
||||
\alias{is_azure_token}
|
||||
\alias{is_azure_v1_token}
|
||||
\alias{is_azure_v2_token}
|
||||
\title{Manage Azure Active Directory OAuth 2.0 tokens}
|
||||
\usage{
|
||||
get_azure_token(resource, tenant, app, password = NULL,
|
||||
|
@ -30,6 +32,10 @@ token_hash(resource, tenant, app, password = NULL, username = NULL,
|
|||
authorize_args = list(), token_args = list())
|
||||
|
||||
is_azure_token(object)
|
||||
|
||||
is_azure_v1_token(object)
|
||||
|
||||
is_azure_v2_token(object)
|
||||
}
|
||||
\arguments{
|
||||
\item{resource}{For AAD v1.0, the URL of your resource host, or a GUID. For AAD v2.0, a character vector of scopes, each consisting of a URL or GUID along with a path designating the access scope. See 'Details' below.}
|
||||
|
@ -58,7 +64,7 @@ is_azure_token(object)
|
|||
|
||||
\item{confirm}{For \code{delete_azure_token}, whether to prompt for confirmation before deleting a token.}
|
||||
|
||||
\item{object}{For \code{is_azure_token}, an R object.}
|
||||
\item{object}{For \code{is_azure_token}, \code{is_azure_v1_token} and \code{is_azure_v2_token}, an R object.}
|
||||
}
|
||||
\description{
|
||||
These functions extend the OAuth functionality in httr for use with Azure Active Directory (AAD).
|
||||
|
@ -103,7 +109,7 @@ To delete \emph{all} cached tokens, use \code{clean_token_directory}.
|
|||
|
||||
\section{Value}{
|
||||
|
||||
For \code{get_azure_token}, an object of class \code{AzureToken} representing the AAD token. For \code{list_azure_tokens}, a list of such objects retrieved from disk.
|
||||
For \code{get_azure_token}, an object of class either \code{AzureTokenV1} or \code{AzureTokenV2} depending on whether the token is for AAD v1.0 or v2.0. For \code{list_azure_tokens}, a list of such objects retrieved from disk.
|
||||
}
|
||||
|
||||
\examples{
|
||||
|
|
Загрузка…
Ссылка в новой задаче