diff --git a/DESCRIPTION b/DESCRIPTION index ccaf6d8..3e5fd39 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AzureAuth Title: Authentication Services for Azure Active Directory -Version: 1.1.0 +Version: 1.1.0.9000 Authors@R: c( person("Hong", "Ooi", , "hongooi@microsoft.com", role = c("aut", "cre")), person("httr development team", role="ctb", comment="Original OAuth listener code"), diff --git a/NAMESPACE b/NAMESPACE index 5761182..2f7582b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,7 @@ export(decode_jwt) export(delete_azure_token) export(format_auth_header) export(get_azure_token) +export(get_managed_token) export(is_azure_token) export(is_azure_v1_token) export(is_azure_v2_token) diff --git a/NEWS.md b/NEWS.md index ed7db07..4baf14e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# AzureAuth 1.1.0.9000 + +* New `get_managed_token` function to obtain a token for a managed identity. Note this only works within a VM, service or container to which an identity has been assigned. + # AzureAuth 1.1.0 * Much improved support for authenticating with a certificate. In the `certificate` argument, specify either the name of a PEM/PFX file, or an AzureKeyVault object representing a cert. diff --git a/R/AzureAuth.R b/R/AzureAuth.R index 4b57e4f..2437152 100644 --- a/R/AzureAuth.R +++ b/R/AzureAuth.R @@ -4,6 +4,7 @@ utils::globalVariables(c("self", "private")) .onLoad <- function(libname, pkgname) { make_AzureR_dir() + options(azure_imds_version="2018-02-01") invisible(NULL) } diff --git a/R/AzureToken.R b/R/AzureToken.R index 208710f..31b04d3 100644 --- a/R/AzureToken.R +++ b/R/AzureToken.R @@ -49,7 +49,8 @@ public=list( device_code=init_devcode, client_credentials=init_clientcred, on_behalf_of=init_clientcred, - resource_owner=init_resowner + resource_owner=init_resowner, + managed=init_managed ) environment(private$initfunc) <- parent.env(environment()) diff --git a/R/initfuncs.R b/R/initfuncs.R index d4a0da6..96a1d03 100644 --- a/R/initfuncs.R +++ b/R/initfuncs.R @@ -73,6 +73,18 @@ init_resowner <- function() } +init_managed <- function() +{ + stopifnot(is.list(self$token_args)) + + uri <- private$aad_endpoint("token") + query <- utils::modifyList(self$token_args, + list(`api-version`=getOption("azure_imds_version"), resource=self$resource)) + + httr::GET(uri, httr::add_headers(metadata="true"), query=query) +} + + listen_for_authcode <- function(url, localhost="127.0.0.1", localport=1410) { # based on httr::oauth_listener diff --git a/R/managed_token.R b/R/managed_token.R new file mode 100644 index 0000000..3bbd282 --- /dev/null +++ b/R/managed_token.R @@ -0,0 +1,8 @@ +#' @rdname get_azure_token +#' @export +get_managed_token <- function(resource, token_args=list()) +{ + auth_type <- "managed" + aad_host <- "http://169.254.169.254/metadata/identity/oauth2" + AzureTokenV1$new(resource, tenant="common", app=NULL, auth_type=auth_type, aad_host=aad_host, token_args=token_args) +} diff --git a/R/token.R b/R/token.R index 4bc1af4..9d18c11 100644 --- a/R/token.R +++ b/R/token.R @@ -1,6 +1,6 @@ #' Manage Azure Active Directory OAuth 2.0 tokens #' -#' These functions extend the OAuth functionality in httr for use with Azure Active Directory (AAD). +#' Use these functions to authenticate with Azure Active Directory (AAD). #' #' @param 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. #' @param tenant Your tenant. This can be a name ("myaadtenant"), a fully qualified domain name ("myaadtenant.onmicrosoft.com" or "mycompanyname.com"), or a GUID. @@ -12,12 +12,14 @@ #' @param aad_host URL for your AAD host. For the public Azure cloud, this is `https://login.microsoftonline.com/`. Change this if you are using a government or private cloud. Can also be a full URL, eg `https://mydomain.b2clogin.com/mydomain/other/path/names/oauth2`. #' @param version The AAD version, either 1 or 2. #' @param authorize_args An optional list of further parameters for the AAD authorization endpoint. These will be included in the request URI as query parameters. Only used if `auth_type="authorization_code"`. -#' @param token_args An optional list of further parameters for the token endpoint. These will be included in the body of the request. +#' @param token_args An optional list of further parameters for the token endpoint. These will be included in the body of the request for `get_azure_token`, or as URI query parameters for `get_managed_token`. #' @param on_behalf_of For the on-behalf-of authentication type, a token. This should be either an AzureToken object, or a string containing the JWT-encoded token itself. #' #' @details #' `get_azure_token` does much the same thing as [httr::oauth2.0_token()], but customised for Azure. It obtains an OAuth token, first by checking if a cached value exists on disk, and if not, acquiring it from the AAD server. `delete_azure_token` deletes a cached token, and `list_azure_tokens` lists currently cached tokens. #' +#' `get_managed_token` is a specialised function to acquire tokens for a _managed identity_. This is an Azure service, such as a VM or container, that has been assigned its own identity and can be granted access permissions like a regular user. The advantage of managed identities over the other authentication methods (see below) is that you don't have to store a secret password, which improves security. Note that `get_managed_token` can only be used from within the managed identity itself. +#' #' The `resource` arg should be a single URL or GUID for AAD v1.0, and a vector of scopes for AAD v2.0. The latter consist of a URL or a GUID, along with a path that designates the scope. If a v2.0 scope doesn't have a path, `get_azure_token` will append the `/.default` path with a warning. A special scope is `offline_access`, which requests a refresh token from AAD along with the access token: without this scope, you will have to reauthenticate if you want to refresh the token. #' #' For B2C logins, the `aad_host` argument can be a full URL including the tenant and arbitrary path components, but excluding the specific endpoint. @@ -142,6 +144,10 @@ #' get_azure_token("https://management.azure.com/", "mytenant", "app_id", #' certificate=cert_assertion("mycert.pem", duration=2*3600) #' +#' +#' # get a token from within a managed service identity (VM, container or service) +#' get_managed_token("https://management.azure.com/") +#' #' } #' @export get_azure_token <- function(resource, tenant, app, password=NULL, username=NULL, certificate=NULL, auth_type=NULL, @@ -161,7 +167,8 @@ select_auth_type <- function(password, username, certificate, auth_type, on_beha if(!is.null(auth_type)) { if(!auth_type %in% - c("authorization_code", "device_code", "client_credentials", "resource_owner", "on_behalf_of")) + c("authorization_code", "device_code", "client_credentials", "resource_owner", "on_behalf_of", + "managed")) stop("Invalid authentication method") return(auth_type) } diff --git a/README.md b/README.md index d6b256c..389fa90 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,14 @@ get_azure_token("myresource", "mytenant", "app_id", password="client_secret", on_behalf_of=token) ``` +Finally, AzureAuth provides `get_managed_token` to obtain tokens from within a managed identity. This is a VM, service or container in Azure that can authenticate as itself, which removes the need to save secret passwords or certificates. + +```r +# run this from within an Azure VM or container for which an identity has been setup +get_managed_token("myresource") +``` + + ## Acknowledgements The AzureAuth interface is based on the OAuth framework in the [httr](https://github.com/r-lib/httr) package, customised and streamlined for Azure. It is an independent implementation of OAuth, but benefited greatly from the work done by Hadley Wickham and the rest of the httr development team. diff --git a/man/get_azure_token.Rd b/man/get_azure_token.Rd index 3d8806e..42587b3 100644 --- a/man/get_azure_token.Rd +++ b/man/get_azure_token.Rd @@ -1,6 +1,7 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/token.R -\name{get_azure_token} +% Please edit documentation in R/managed_token.R, R/token.R +\name{get_managed_token} +\alias{get_managed_token} \alias{get_azure_token} \alias{delete_azure_token} \alias{clean_token_directory} @@ -11,6 +12,8 @@ \alias{is_azure_v2_token} \title{Manage Azure Active Directory OAuth 2.0 tokens} \usage{ +get_managed_token(resource, token_args = list()) + get_azure_token(resource, tenant, app, password = NULL, username = NULL, certificate = NULL, auth_type = NULL, aad_host = "https://login.microsoftonline.com/", version = 1, @@ -40,6 +43,8 @@ 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.} +\item{token_args}{An optional list of further parameters for the token endpoint. These will be included in the body of the request for \code{get_azure_token}, or as URI query parameters for \code{get_managed_token}.} + \item{tenant}{Your tenant. This can be a name ("myaadtenant"), a fully qualified domain name ("myaadtenant.onmicrosoft.com" or "mycompanyname.com"), or a GUID.} \item{app}{The client/app ID to use to authenticate with.} @@ -58,8 +63,6 @@ is_azure_v2_token(object) \item{authorize_args}{An optional list of further parameters for the AAD authorization endpoint. These will be included in the request URI as query parameters. Only used if \code{auth_type="authorization_code"}.} -\item{token_args}{An optional list of further parameters for the token endpoint. These will be included in the body of the request.} - \item{on_behalf_of}{For the on-behalf-of authentication type, a token. This should be either an AzureToken object, or a string containing the JWT-encoded token itself.} \item{hash}{The MD5 hash of this token, computed from the above inputs. Used by \code{delete_azure_token} to identify a cached token to delete.} @@ -69,11 +72,13 @@ is_azure_v2_token(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). +Use these functions to authenticate with Azure Active Directory (AAD). } \details{ \code{get_azure_token} does much the same thing as \code{\link[httr:oauth2.0_token]{httr::oauth2.0_token()}}, but customised for Azure. It obtains an OAuth token, first by checking if a cached value exists on disk, and if not, acquiring it from the AAD server. \code{delete_azure_token} deletes a cached token, and \code{list_azure_tokens} lists currently cached tokens. +\code{get_managed_token} is a specialised function to acquire tokens for a \emph{managed identity}. This is an Azure service, such as a VM or container, that has been assigned its own identity and can be granted access permissions like a regular user. The advantage of managed identities over the other authentication methods (see below) is that you don't have to store a secret password, which improves security. Note that \code{get_managed_token} can only be used from within the managed identity itself. + The \code{resource} arg should be a single URL or GUID for AAD v1.0, and a vector of scopes for AAD v2.0. The latter consist of a URL or a GUID, along with a path that designates the scope. If a v2.0 scope doesn't have a path, \code{get_azure_token} will append the \code{/.default} path with a warning. A special scope is \code{offline_access}, which requests a refresh token from AAD along with the access token: without this scope, you will have to reauthenticate if you want to refresh the token. For B2C logins, the \code{aad_host} argument can be a full URL including the tenant and arbitrary path components, but excluding the specific endpoint. @@ -199,6 +204,10 @@ get_azure_token("https://management.azure.com/", "mytenant", "app_id", get_azure_token("https://management.azure.com/", "mytenant", "app_id", certificate=cert_assertion("mycert.pem", duration=2*3600) + +# get a token from within a managed service identity (VM, container or service) +get_managed_token("https://management.azure.com/") + } } \seealso{