Refresh token from Azure using Shiny

Viewed 204

I wrote a web application using Shiny package of R. The whole application consists of three main files. ui.R, Server.R and app.R.

app.R is the files where I execute in the Azure Kubernetics and it calls subsequently other two files.I'm also managing the Azure authentication in this file. So my app.R looks like :

load.lib <- c("AzureAuth","shiny","shinyjs","httr","config")

# install.lib <- load.lib[!load.lib %in% installed.packages()]
# for(lib in install.lib) install.packages(lib,dependencies=TRUE)
sapply(load.lib,library,character=TRUE)

AADConfig <- config::get(file = "config.yml")

resourceid = AADConfig$resourceid   # Application (client) id
tenant = AADConfig$tenant  # Directory(tenant) id
app = AADConfig$app        # Application (client) id
pass = AADConfig$secret

redirect <- "https://myapp.com"

# resource <- c("https://management.azure.com/.default", resourceid)
clean_url_js <- sprintf(
  "
    $(document).ready(function(event) {
      const nextURL = '%s';
      const nextTitle = 'My new page title';
      const nextState = { additionalInformation: 'Updated the URL with JS' };
      // This will create a new entry in the browser's history, without reloading
      window.history.pushState(nextState, nextTitle, nextURL);
    });
    ", redirect
)

###############Importing the app R files#########
# load ui elements
source("ui.R")
# load server function
source("server.R")
#################################################
ui_func <- function(req)
{
  opts <- parseQueryString(req$QUERY_STRING)
  if(is.null(opts$code))
  {
    auth_uri <- AzureAuth::build_authorization_uri(resourceid, tenant, app, redirect_uri=redirect)
    redir_js <- sprintf("location.replace(\"%s\");", auth_uri)
    tags$script(HTML(redir_js))
  }
  else ui
}

server_func <- function(input, output, session)
{

  shinyjs::runjs(clean_url_js)

  opts <- parseQueryString(isolate(session$clientData$url_search))
  if(is.null(opts$code))
    return()

   Token <- AzureAuth::get_azure_token(resourceid, 
                               tenant, 
                               app,
                               password = pass,
                               auth_type="authorization_code",
                               authorize_args=list(redirect_uri=redirect),
                               use_cache=TRUE,
                               auth_code = opts$code
                               )
  
  
  access_role <- AzureAuth::decode_jwt(Token)$payload$groups
  
  return(server(input, output, access_role))
   
}



# Run the application
shiny::shinyApp(ui = ui_func, server = server_func)

I can get the Token and the application runs fine but there is a one hour timeout for online Shiny apps, which is standard I think from the Azure tokens.

I know I can get my refresh token in Token$credentials$refresh_token but I don't know how to use it to get a new token as after 60 minutes the screen greys out even while users are in the web app.

Note: I decode the Token I get in the app.R and based on users access, I query the database in the server.R. If there is a more efficient way of doing it, please advise.

1 Answers

The default lifetime of access token provided by Azure AD is ranging between 60-90 minutes to avoid token from various attacks.

However, When the access token expires, the client must use the refresh token (usually silently) to acquire a new refresh token maintain the session. The lifetime of refresh token is typically longer than access token (nearly 90 days).

To get the refresh token along with access token, you need to request offline_access scope while requesting the token.

Shiny package of R provides get_azure_function to authenticate with Azure active directory which has different parameters to pass to get the access token. Here, in the resource parameter of Azure AD v2.0 you can provide multiple scopes, where each scope consists of a URL or GUID along with a path that designates the type of access requested.

You need to pass special scope offline_access in resource parameter, which requests a refresh token from Azure AD along with the access token.

With this scope, you won’t need to reauthenticate to get the token again. If the token's credentials contain a refresh token, A token object can be refreshed automatically by calling its refresh() method.

    #Example to authenticate using Azure resource manager along with refresh token.
    
    token2 <- get_azure_token(c("https://management.azure.com/.default", "offline_access"),
        "mytenant", "app_id", version=2)
    
    
    #requesting multiple scopes (Microsoft Graph)along with refresh token with AAD 2.0
    
    get_azure_token(c("https://graph.microsoft.com/User.Read.All",
                      "https://graph.microsoft.com/User.ReadWrite.All",
                      "https://graph.microsoft.com/Directory.ReadWrite.All",
                      "offline_access"),
        "mytenant", "app_id", version=2)
Related