Skip to contents

Create provider settings for Microsoft Entra ID. Choose which accounts may sign in with tenant, then pass the provider and your own registered app's credentials to oauth_client().

Usage

oauth_provider_microsoft(
  name = "microsoft",
  tenant = c("common", "organizations", "consumers"),
  id_token_validation = NULL,
  userinfo_required = TRUE
)

Arguments

name

Optional friendly name for the provider. Defaults to "microsoft"

tenant

Tenant identifier ("common", "organizations", "consumers", or directory GUID). Defaults to "common"

id_token_validation

Optional override (logical). If NULL (default), it's enabled automatically when tenant looks like a GUID or one of the Microsoft alias tenants (common, organizations, consumers). common and organizations use Microsoft's tenant-independent issuer and signing-key validation rules; consumers uses the stable consumer tenant issuer

userinfo_required

Retrieve profile information from Microsoft Graph. Set FALSE for an API token with a different audience. Manual get_userinfo() calls require the complete validated OAuthToken when OIDC validation is enabled.

Value

OAuthProvider object configured for Microsoft identity platform

Details

Use a directory (tenant) ID to target one organization. "organizations" allows work or school accounts, "consumers" allows personal Microsoft accounts, and "common" allows both. Your app registration and app access rules must also permit the intended accounts.

Profiles are fetched from Microsoft Graph by default. Set userinfo_required = FALSE for an API token that cannot call Graph.

Advanced identity settings

ID token validation is enabled for these tenant choices. For a directory ID, the issuer must match that directory. "common" and "organizations" use Microsoft's tenant-independent issuer template and signing-key issuer rules. "consumers" uses the consumer tenant issuer. The helper restricts ID token algorithms to RS256.

Setting id_token_validation = FALSE disables ID token and nonce checks and leaves OAuth plus any configured profile retrieval. Tenant domains and other unrecognized tenant identifiers require that setting; directory GUIDs and the aliases above support OIDC validation automatically.

Examples

if (
  # Example requires configured Microsoft Entra ID (Azure AD) tenant:
  nzchar(Sys.getenv("MS_TENANT")) &&
    interactive() &&
    requireNamespace("later", quietly = TRUE)
) {
  library(shiny)
  library(shinyOAuth)

  # Configure provider and client (Microsoft Entra ID with your tenant)
  client <- oauth_client(
    provider = oauth_provider_microsoft(
      # Provide your own tenant ID here (set as environment variable MS_TENANT)
      tenant = Sys.getenv("MS_TENANT")
    ),
    # Azure CLI public-client app ID; the tenant must permit this app.
    # For your deployed app, use your own registration and redirect URI:
    client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
    client_secret = "",
    redirect_uri = "http://localhost:8100",
    scopes = c("openid", "profile", "email")
  )

  # UI
  ui <- oauth_ui(
    fluidPage(
      h3("OAuth demo (Microsoft Entra ID)"),
      uiOutput("oauth_error"),
      tags[["hr"]](),
      h4("Auth object (summary)"),
      verbatimTextOutput("auth_print"),
      tags[["hr"]](),
      h4("User info"),
      verbatimTextOutput("user_info")
    ),
    id = "auth",
    client = client
  )

  # Server
  server <- function(input, output, session) {
    auth <- oauth_module_server("auth", client)

    output[["auth_print"]] <- renderText({
      authenticated <- auth[["authenticated"]]
      tok <- auth[["token"]]
      err <- auth[["error"]]

      paste0(
        "Authenticated?",
        if (isTRUE(authenticated)) " YES" else " NO",
        "\n",
        "Has token? ",
        if (!is.null(tok)) "YES" else "NO",
        "\n",
        "Has error? ",
        if (!is.null(err)) "YES" else "NO",
        "\n\n",
        "Token present: ",
        !is.null(tok),
        "\n",
        "Has refresh token: ",
        !is.null(tok) && isTRUE(nzchar(tok@refresh_token)),
        "\n",
        "Has ID token: ",
        !is.null(tok) && !is.na(tok@id_token),
        "\n",
        "Expires at: ",
        if (!is.null(tok)) tok@expires_at else "N/A"
      )
    })

    output[["user_info"]] <- renderPrint({
      req(auth[["authenticated"]])
      auth[["token"]]@userinfo
    })

    observeEvent(
      list(auth[["error"]], auth[["error_description"]]),
      {
        if (interactive() && !is.null(auth[["error_description"]])) {
          rlang::inform(c(
            "OAuth error details",
            "i" = paste0("error: ", auth[["error"]]),
            "i" = paste0("error_description: ", auth[["error_description"]])
          ))
        }
      },
      ignoreInit = TRUE
    )

    output[["oauth_error"]] <- renderUI({
      if (is.null(auth[["error"]])) {
        return(NULL)
      }

      msg <- if (identical(auth[["error"]], "access_denied")) {
        "Sign-in was canceled or denied. Please try again."
      } else {
        "Authentication failed. Please try again."
      }

      div(class = "alert alert-danger", role = "alert", msg)
    })
  }

  # Open localhost:8100 to match the Azure CLI app's registered redirect URI
  # and keep browser cookies on the same host. Schedule the browser launch
  # before runApp() starts blocking R.
  later::later(
    function() {
      utils::browseURL("http://localhost:8100")
    },
    delay = 0.25
  )

  # Run app
  runApp(shinyApp(ui, server), port = 8100, launch.browser = FALSE)
}