Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get a token object from a raw refresh token string #74

Open
felixkgr opened this issue Dec 16, 2022 · 3 comments
Open

Cannot get a token object from a raw refresh token string #74

felixkgr opened this issue Dec 16, 2022 · 3 comments

Comments

@felixkgr
Copy link

I have a raw OAuth 2.0 refresh token string (let's say from a previous session or obtained by other means) and would like to get a Azure Token object (including a new access token and refresh token) without requiring any user interaction.

As I only have the raw refresh token string and no AzureToken object, calling refresh() is not possible at that time.
And trying to create an AzureToken directly results in following error message:
Do not call this constructor directly; use get_azure_token() instead

However, the get_azure_token() function does not provide an option to pass a refresh token.

Could you provide a way to get an AzureToken object from an OAuth 2.0 refresh token string?

@marcolindner-de
Copy link

marcolindner-de commented Aug 1, 2023

In this context I would like to add:
I think it would make sense to implement a refresh_token flow in general, in which get_azure_token() can either be passed a refresh token (string) directly or a previously received AzureToken to get a new token through refresh. A different scope than that of the original token can also be set.

Background:
In an R package, I would like to get a token from the user once (interactively, e.g. via device code for a public client app) in order to then receive further tokens for various resources (e.g. Azure Storage and Azure Key Vault) on behalf of the user without additional user interaction. The on_behalf_of flow is out of the question here, since it's a public client (R package) and I can't specify a secret in the R package. In order to achieve this, you currently have to use the refresh_token flow / grant type, because the refresh token is not tied to the resource but only to the user and the client.

You could do something like this as a workaround to achieve this using the AzureAuth library including it's caching functionality:

resource <- "https://storage.azure.com/.default offline_access"
version <- 2
use_cache <- TRUE

tenant_id <- "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
public_client_app_id <- "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

# Get token for R client app from user (device_code flow)
token_app <-
  AzureAuth::get_azure_token(
    resource =
	  if (version == 1) public_client_app_id
	  else sprintf("%s/.default offline_access", client_app_id),
    version = version,
    tenant = tenant_id,
    app = public_client_app_id,
    auth_type = "device_code",
    use_cache = use_cache
  ) |> pkgcond::suppress_messages(pattern = "Loading cached token")

# Clone app token object to create token object for target resource
token <- token_app$clone(deep = TRUE)
if(version == 1) token$resource <- resource
else token$scope <- resource

# Get new token
if(use_cache && (token$hash() %in% names(AzureAuth::list_azure_tokens()))) {
  # Token already cached, just use AzureAuth::get_azure_token()
  token <-
    AzureAuth::get_azure_token(
	  resource = resource,
	  tenant = token$tenant,
	  app = token$client$client_id,
	  auth_type = token$auth_type,
	  aad_host = token$aad_host,
	  version = version,
	  authorize_args = token$authorize_args,
	  token_args = token$token_args,
	  use_cache = use_cache
    ) |> pkgcond::suppress_messages(pattern = "Loading cached token")
  } else {
  # Token not yet cached or no cache used, refresh token
  # (requests new token and caches it if desired)
  token <- token$refresh()
}

# use token to access the resource

But this is kind of tricky so it would be great if get_access_token() would support an auth_type = refresh_token and an additional parameter like refresh_token = "<token string or AzureToken object>" and accept using a different resource/scope than the original token.

@hongooi73
Copy link
Collaborator

You shouldn't need the if there. Just call get_azure_token with the correct arguments and it will be either be retrieved from the cache, or a new token requested.

@marcolindner-de
Copy link

Yes, but I try to prevent a new request for the second token because it would use the device_code flow (cloned from the first token) and this would lead to user interaction again. So the if condition is to only call get_access_token, when the token is already cached to prevent user interaction for the cloned token.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants