Using Microsoft365R in a Shiny app

Hong Ooi

This vignette describes how to incorporate Microsoft365R and interactive authentication with Azure Active Directory (AAD) into a Shiny web app. There are a few steps involved:

App registration

The default Microsoft365R app registration only works when the package is used on a local machine; it does not support running in a remote server. Because of this, when you use Microsoft365R inside a Shiny app, you (or your friendly local sysadmin) must register that app in AAD.

The main things to set in your app registration are:

The following pages at the AAD documentation will be helpful:

Shiny code skeleton

Below is a basic app that logs the user in, retrieves their OneDrive, and lists the contents of the root folder.

One thing to note is that the regular Microsoft365R client functions like get_sharepoint_site, get_team etc are intended for use on a local machine. While they will still work when called in a web app, it’s a better idea to call the underlying R6 methods directly: Microsoft365R extends AzureGraph with several R6 classes and methods, which do the actual work of interacting with the Microsoft 365 REST API.

Here, we call the get_drive() method for the AzureGraph::az_user class, which retrieves the OneDrive for a user. For more information, see the online help page in R for the Microsoft365R “add_methods” topic: ?add_methods.

library(AzureAuth)
library(AzureGraph)
library(Microsoft365R)
library(shiny)

tenant <- "your-tenant-here"

# the application/client ID of the app registration you created in AAD
# - not to be confused with the 'object ID' or 'service principal ID'
app <- "your-app-id-here"

# the address of your app: also the redirect URI of your app registration
# - AAD allows only HTTPS for non-localhost redirects, not HTTP
redirect <- "https://example.com/mysite"
port <- httr::parse_url(redirect)$port
options(shiny.port=if(is.null(port)) 443 else as.numeric(port))

# if your app reg has a 'webapp' redirect, it requires a client secret (password)
# - you should NEVER put secrets in code: here we get it from an environment variable
# - leave the environment variable unset if you have a 'desktop & mobile' redirect
pwd <- Sys.getenv("EXAMPLE_SHINY_CLIENT_SECRET", "")
if(pwd == "") pwd <- NULL

# get the Graph permissions listed for the app, plus an ID token
resource <- c("https://graph.microsoft.com/.default", "openid")

# a simple UI: display the user's OneDrive
ui <- fluidPage(
    verbatimTextOutput("drv")
)

ui_func <- function(req)
{
    opts <- parseQueryString(req$QUERY_STRING)
    if(is.null(opts$code))
    {
        auth_uri <- build_authorization_uri(resource, tenant, app, redirect_uri=redirect, version=2)
        redir_js <- sprintf("location.replace(\"%s\");", auth_uri)
        tags$script(HTML(redir_js))
    }
    else ui
}

server <- function(input, output, session)
{
    opts <- parseQueryString(isolate(session$clientData$url_search))
    if(is.null(opts$code))
        return()

    token <- get_azure_token(resource, tenant, app, password=pwd, auth_type="authorization_code",
                             authorize_args=list(redirect_uri=redirect), version=2,
                             use_cache=FALSE, auth_code=opts$code)

    # display the contents of the user's OneDrive root folder
    drv <- ms_graph$
        new(token=token)$
        get_user()$
        get_drive()
    output$drv <- renderPrint(drv$list_files())
}

shinyApp(ui_func, server)