diff --git a/DESCRIPTION b/DESCRIPTION index 91d318d..8f5033d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AzureAuth Title: Authentication Services for Azure Active Directory -Version: 1.3.4 +Version: 1.3.5 Authors@R: c( person("Hong", "Ooi", , "hongooi73@gmail.com", role = c("aut", "cre")), person("Tyler", "Littlefield", role="ctb"), @@ -33,5 +33,6 @@ Suggests: shinyjs, AzureRMR, AzureGraph +Encoding: UTF-8 Roxygen: list(markdown=TRUE, r6=FALSE) -RoxygenNote: 7.3.2 +Config/roxygen2/version: 8.1.0 diff --git a/NAMESPACE b/NAMESPACE index 583953d..7a9c104 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,11 +1,18 @@ # Generated by roxygen2: do not edit by hand +S3method(build_assertion,cert_assertion) +S3method(build_assertion,character) +S3method(build_assertion,default) +S3method(build_assertion,stored_cert) S3method(decode_jwt,AzureToken) S3method(decode_jwt,Token) S3method(decode_jwt,character) S3method(extract_jwt,AzureToken) S3method(extract_jwt,Token) S3method(extract_jwt,character) +S3method(sign_assertion,character) +S3method(sign_assertion,openssl_cert_pair) +S3method(sign_assertion,stored_cert) export(AzureManualToken) export(AzureR_dir) export(AzureToken) diff --git a/NEWS.md b/NEWS.md index 2d1e3e1..284c44f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# AzureAuth 1.3.5 + +- Fix to `get_managed_token` to handle host URLs that already have the `/token` endpoint included. + # AzureAuth 1.3.4 - New `get_manual_token` function to create a token object from an external token string. Thanks to @falbukrek. diff --git a/R/cert_creds.R b/R/cert_creds.R index c7d3cff..e1cdc0c 100644 --- a/R/cert_creds.R +++ b/R/cert_creds.R @@ -39,12 +39,14 @@ build_assertion <- function(assertion, ...) } +#' @export build_assertion.stored_cert <- function(assertion, ...) { build_assertion(cert_assertion(assertion), ...) } +#' @export build_assertion.character <- function(assertion, ...) { pair <- read_cert_pair(assertion) @@ -52,6 +54,7 @@ build_assertion.character <- function(assertion, ...) } +#' @export build_assertion.cert_assertion <- function(assertion, tenant, app, aad_host, version, ...) { url <- httr::parse_url(aad_host) @@ -72,6 +75,7 @@ build_assertion.cert_assertion <- function(assertion, tenant, app, aad_host, ver } +#' @export build_assertion.default <- function(assertion, ...) { if(is.null(assertion)) @@ -86,6 +90,7 @@ sign_assertion <- function(certificate, claim, size) } +#' @export sign_assertion.stored_cert <- function(certificate, claim, size) { kty <- certificate$policy$key_props$kty # key type determines signing alg @@ -100,6 +105,7 @@ sign_assertion.stored_cert <- function(certificate, claim, size) } +#' @export sign_assertion.openssl_cert_pair <- function(certificate, claim, size) { alg <- if(inherits(certificate$key, "rsa")) @@ -115,6 +121,7 @@ sign_assertion.openssl_cert_pair <- function(certificate, claim, size) } +#' @export sign_assertion.character <- function(certificate, claim, size) { pair <- read_cert_pair(certificate) diff --git a/R/managed_token.R b/R/managed_token.R index 18ba210..0754956 100644 --- a/R/managed_token.R +++ b/R/managed_token.R @@ -3,5 +3,9 @@ get_managed_token <- function(resource, token_args=list(), use_cache=NULL) { aad_host <- Sys.getenv("MSI_ENDPOINT", "http://169.254.169.254/metadata/identity/oauth2") + + # deal with situation where host url string already contains '/token' + aad_host <- sub("/token$", "", aad_host) + AzureTokenManaged$new(resource, aad_host, token_args=token_args, use_cache=use_cache) } diff --git a/R/token_manual.R b/R/token_manual.R index 7eaa844..844b526 100644 --- a/R/token_manual.R +++ b/R/token_manual.R @@ -2,17 +2,18 @@ #' #' Create an Azure token object from a pre-existing access token string. This is useful #' when you have obtained a token externally (e.g., via Azure CLI, Python, or another -#' authentication mechanism) and want to use it with the AzureR ecosystem. +#' authentication mechanism) and want to use it with the AzureR ecosystem. Rather than +#' calling the new() method directly, tokens should be created via [get_manual_token()]. #' #' @docType class #' @section Methods: -#' \itemize{ -#' \item \code{new(token, type, tenant, resource)}: Initialize a new manual token object. -#' \item \code{refresh()}: Cannot refresh a manual token; issues a warning and returns self. -#' \item \code{validate()}: Checks if the token has expired based on JWT claims. -#' \item \code{can_refresh()}: Returns FALSE since manual tokens cannot be refreshed. -#' \item \code{cache()}: No-op; manual tokens are not cached. -#' } +#' +#' This section documents how the methods for manual tokens differ from other token objects. +#' +#' - `refresh`: Manual tokens cannot be refreshed; you must create a new token object. +#' - `can_refresh`: Always returns FALSE for manual tokens. +#' - `cache`: Manual tokens are not cached; this method does nothing. +#' - `hash`: The hash is based on the token string itself, rather than the R-level metadata. #' #' @details #' The \code{AzureManualToken} class provides a way to wrap an externally-obtained access @@ -58,11 +59,6 @@ AzureManualToken <- R6::R6Class("AzureManualToken", inherit = AzureToken, public = list( - #' @description Initialize a manual token from a raw access token string. - #' @param token A character string containing the access token. - #' @param type The token type, usually "Bearer". - #' @param tenant Optional tenant ID. If NULL, extracted from JWT claims. - #' @param resource Optional resource/audience. If NULL, extracted from JWT claims. initialize = function(token, type = "Bearer", tenant = NULL, resource = NULL) { if(missing(token) || is.null(token) || !is.character(token) || nchar(token) == 0) @@ -173,30 +169,22 @@ public = list( invisible(self) }, - #' @description Refresh the token. Manual tokens cannot be refreshed. - #' @return Returns self invisibly. refresh = function() { invisible(self) }, - #' @description Check if this token can be refreshed. - #' @return Always returns FALSE for manual tokens. can_refresh = function() { FALSE }, - #' @description Cache the token. Manual tokens are not cached. - #' @return Returns NULL invisibly. cache = function() { # Do not cache manual tokens - they are managed externally invisible(NULL) }, - #' @description Compute a hash for this token. - #' @return An MD5 hash string based on the token content. hash = function() { # Hash based on the token string itself @@ -209,7 +197,6 @@ public = list( paste(openssl::md5(msg[-(1:14)]), collapse = "") }, - #' @description Print the token object. print = function() { cat(format_auth_header(self)) diff --git a/man/AzureManualToken.Rd b/man/AzureManualToken.Rd index 4f2e21f..3482426 100644 --- a/man/AzureManualToken.Rd +++ b/man/AzureManualToken.Rd @@ -7,42 +7,13 @@ \format{ An R6 object of class \code{AzureManualToken}, inheriting from \code{AzureToken}. } -\arguments{ -\item{token}{A character string containing the access token.} - -\item{type}{The token type, usually "Bearer".} - -\item{tenant}{Optional tenant ID. If NULL, extracted from JWT claims.} - -\item{resource}{Optional resource/audience. If NULL, extracted from JWT claims.} -} -\value{ -Returns self invisibly. - -Always returns FALSE for manual tokens. - -Returns NULL invisibly. - -An MD5 hash string based on the token content. -} \description{ -Initialize a manual token from a raw access token string. - -Refresh the token. Manual tokens cannot be refreshed. - -Check if this token can be refreshed. - -Cache the token. Manual tokens are not cached. - -Compute a hash for this token. - -Print the token object. -} -\details{ Create an Azure token object from a pre-existing access token string. This is useful when you have obtained a token externally (e.g., via Azure CLI, Python, or another -authentication mechanism) and want to use it with the AzureR ecosystem. - +authentication mechanism) and want to use it with the AzureR ecosystem. Rather than +calling the new() method directly, tokens should be created via \code{\link[=get_manual_token]{get_manual_token()}}. +} +\details{ The \code{AzureManualToken} class provides a way to wrap an externally-obtained access token string so it can be used with packages like \code{AzureGraph}, \code{AzureRMR}, and other AzureR family packages that expect an \code{AzureToken} object. @@ -57,12 +28,13 @@ object with a fresh token string. } \section{Methods}{ + +This section documents how the methods for manual tokens differ from other token objects. \itemize{ -\item \code{new(token, type, tenant, resource)}: Initialize a new manual token object. -\item \code{refresh()}: Cannot refresh a manual token; issues a warning and returns self. -\item \code{validate()}: Checks if the token has expired based on JWT claims. -\item \code{can_refresh()}: Returns FALSE since manual tokens cannot be refreshed. -\item \code{cache()}: No-op; manual tokens are not cached. +\item \code{refresh}: Manual tokens cannot be refreshed; you must create a new token object. +\item \code{can_refresh}: Always returns FALSE for manual tokens. +\item \code{cache}: Manual tokens are not cached; this method does nothing. +\item \code{hash}: The hash is based on the token string itself, rather than the R-level metadata. } } diff --git a/man/AzureToken.Rd b/man/AzureToken.Rd index d18cb83..1988b3a 100644 --- a/man/AzureToken.Rd +++ b/man/AzureToken.Rd @@ -14,7 +14,7 @@ An R6 object representing an Azure Active Directory token and its associated credentials. \code{AzureToken} is the base class, and the others inherit from it. } \description{ -Azure OAuth 2.0 token classes, with an interface based on the \link[httr:Token-class]{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()}}. +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}{ @@ -27,5 +27,5 @@ Azure OAuth 2.0 token classes, with an interface based on the \link[httr:Token-c } \seealso{ -\link{get_azure_token}, \link[httr:Token-class]{httr::Token} +\link{get_azure_token}, \link[httr:Token]{httr::Token} } diff --git a/man/get_azure_token.Rd b/man/get_azure_token.Rd index 8cad17a..0b7f783 100644 --- a/man/get_azure_token.Rd +++ b/man/get_azure_token.Rd @@ -348,7 +348,7 @@ httr::GET("https://myresource/path/for/call", header, ...) } } \seealso{ -\link{AzureToken}, \link[httr:oauth2.0_token]{httr::oauth2.0_token}, \link[httr:Token-class]{httr::Token}, \link{cert_assertion}, +\link{AzureToken}, \link[httr:oauth2.0_token]{httr::oauth2.0_token}, \link[httr:Token]{httr::Token}, \link{cert_assertion}, \link{build_authorization_uri}, \link{get_device_creds} \href{https://learn.microsoft.com/en-us/azure/active-directory/develop/}{Azure Active Directory for developers},