Search code examples
roauth-2.0access-tokenhttr

How do you retrieve the URL for authentication in R's httr::oauth2.0_token?


I use the code below to get API access to my Microsoft Dynamics Environment where data is stored.

require(httr)

dataverse_api = oauth_endpoint(request = NULL, 
  authorize = "https://login.microsoftonline.com/[REDACTED]/oauth2/v2.0/authorize",
  access = "https://login.microsoftonline.com/[REDACTED]/oauth2/v2.0/token",
  base_url = "https://login.microsoftonline.com/common/oauth2/authorize?resource=https://[REDACTED].crm4.dynamics.com")

API.Key = "[REDACTED]"
API.Secret = "[REDACTED]"

App = oauth_app("[REDACTED]", key = API.Key, secret = API.Secret)

API.token = oauth2.0_token(dataverse_api, App, scope = "https://[REDACTED].crm4.dynamics.com/user_impersonation", cache = FALSE, use_oob = FALSE)

API.AuthKey = API.token$credentials$access_token

If I run the code in RStudio, it points my browser to a URL and I get the response:

Waiting for authentication in browser...
Press Esc/Ctrl + C to abort
Authentication complete

However, if I run the code in the R Terminal, it returns the URL into the R Terminal that I then need to copy and paste into the browser to complete the authentication.

What I want to know is... what is RStudio doing to automate this step? How does RStudio know the URL to open? How can I extract that URL and put it into a browseURL function to automate this last step? Here is what I've tried...

  • I've looked through all of the parameters of my API.token by typing API.token$ and continuing down the path to explore things like cache_path, private_key, endpoint$authorize, params$.... In all of these parameters I haven't found anything that matches the URL that gets opened in my web browser from RStudio.
  • I've tried setting use_oob=TRUE, but this actually provides a 400-type response.
  • I've looked through ?oauth2.0_token and the link to Token() provided in the Details section, which I didn't find helpful.

I'm expecting that somewhere there is a parameter that I can extract and put it into a code like this:

browseURL(paste("http://localhost:1410/?code=","INSERT PARAMETER HERE",sep = ""))

Does anyone know how to get that parameter?


Solution

  • The relevant code is at the bottom of oauth-init.R. But like a lot in {httr} it is complicated.

    The docs of {AzureAuth} are clearer. If you're in RStudio both packages are using the authorization_code flow and use a small {httpuv} web server to catch the redirect. You can see the required request here on the help page. Here is the request URL:

    https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/authorize?
    client_id={client id}
    &response_type=code
    &redirect_uri={http://where-you-listen-for-the-response.might-be-localhost escaped for use in URLs}
    &response_mode=query
    &scope={scope escaped for use in URLs}
    &state=12345
    

    If you open the above in a browser, Azure will sign you in for the app specified in client_id with the scopes in the request and then redirect you to redirect_uri with a code. If you have a web server running on redirect_uri (that's where the httpuv comes in) and if the redirect_uri is whitelisted for that Azure App, your R program receives that code and can then use it to acquire an access token from the token endpoint.

    This constructs the URL. You need something to manage the states.

    url <-
        httr::modify_url(
           endpoint_authorize,
           query = list(client_id = client_id,
                        response_type = "code",
                        redirect_uri = redirect_uri,
                        scope = scope,
                        response_mode = "query",
                        state = get_state_key())
        ) %>%
        htmltools::HTML()
    
    

    From the terminal it uses the device_code auth flow that does not need a redirect. Instead you act as your own "network link" by copying the code into the browser.

    Depending on your use case those two authorization flows might be overkill. It is meant as a way for a server to securely acquire a token that allows it to access resources in your name. If you're only getting a token for your local machine and you have control over what flows the app accepts (because it is your own), that can generally be handled by the client_credentials flow.

    If you look at the above-mentioned {AzureAuth} you can see the alternatives. Googling for "Azure" and the names of the flows will lead you to the docs on Azure.