Introduction

Most major mail providers have moved to OAuth2.0 as their preferred (and, in many cases, only) authentication method for IMAP. Instead of sending your password, you send a short-lived access token that authorizes a specific scope of access to your mailbox.

This vignette shows the exact steps and R code to obtain, use, and refresh an OAuth2.0 access token for the Gmail IMAP server. The same logic applies to other providers (Outlook/Office 365, Yahoo, AOL, …) by swapping the authorization endpoints and scope. The reference for Google’s flow is https://developers.google.com/identity/protocols/oauth2.

We use the httr package to run the OAuth2.0 flow and jsonlite to read the credentials file, so install them first if needed:

install.packages(c("httr", "jsonlite"))

IMPORTANT — libcurl version. The libcurl feature that transmits the bearer token is only reliable on libcurl >= 7.65.0 (released 2019-05-22). Check the version the curl R package is linked against:

curl::curl_version()$version
## [1] "7.81.0"

If it is older, update libcurl (and reinstall curl) before continuing, otherwise you may get a SASL error during authentication.

The whole process is:

  1. create a Google Cloud project;
  2. configure the OAuth consent screen (scope + test user);
  3. create a Desktop app OAuth client and download its JSON credentials;
  4. obtain an access token in R;
  5. open the IMAP connection with configure_imap(xoauth2_bearer = ...);
  6. refresh the token when it expires.

Steps 1–3 are done once, in the browser; steps 4–6 are R code.

Step 1 - Create a Google Cloud project

  1. Go to https://console.cloud.google.com/ and sign in with the Google account whose mailbox you want to access.
  2. In the project selector at the top, click New Project, give it a name (e.g. mRpostman), and click Create.
  3. Make sure this new project is the one selected in the top bar.

You do not need to enable the Gmail API: IMAP access is granted by the OAuth scope https://mail.google.com/, not by the Gmail API. IMAP is enabled by default on Gmail accounts.

Step 3 - Create the OAuth client credentials

  1. Go to APIs & Services > Credentials.
  2. Click + Create credentials > OAuth client ID.
  3. For Application type, select Desktop app (this is important — a Web application client will fail with redirect_uri_mismatch in the flow below).
  4. Give it a name and click Create.
  5. Click Download JSON and save the file locally. Keep it private — it contains your client secret.

The downloaded file looks like this (an "installed" client):

{"installed":{"client_id":"XXXX.apps.googleusercontent.com",
  "project_id":"your-project","auth_uri":"https://accounts.google.com/o/oauth2/auth",
  "token_uri":"https://oauth2.googleapis.com/token",
  "client_secret":"GOCSPX-XXXX","redirect_uris":["http://localhost"]}}

Read the client id and secret from it in R:

cred <- jsonlite::fromJSON("path/to/client_secret_XXXX.json")$installed

Step 4 - Obtain an access token

Below are two ways to get a token. Option A is the shortest and works out of the box on most desktops. Use Option B if Option A fails to start its local web server (common inside RStudio, or on headless/remote machines).

Both request access_type = "offline" so that a refresh token is also returned (see Step 6).

Option B - Manual loopback flow (no local server)

This avoids the local callback server entirely: you open the authorization URL, approve access, and paste back the code shown in the browser’s address bar.

library(httr)

redirect_uri <- "http://localhost"
scope        <- "https://mail.google.com/"

auth_url <- modify_url("https://accounts.google.com/o/oauth2/auth",
  query = list(client_id     = cred$client_id,
               redirect_uri  = redirect_uri,
               response_type = "code",
               scope         = scope,
               access_type   = "offline",
               prompt        = "consent"))

browseURL(auth_url)

After you approve, the browser is redirected to http://localhost/?code=...&scope=.... The page will fail to load (“unable to connect”) — that is fine. Copy the value between code= and &scope from the address bar and paste it below (authorization codes are single-use and expire in minutes, so exchange it right away):

code_in <- "PASTE_THE_CODE_HERE"

resp <- POST("https://oauth2.googleapis.com/token", encode = "form",
  body = list(code          = code_in,
              client_id     = cred$client_id,
              client_secret = cred$client_secret,
              redirect_uri  = redirect_uri,
              grant_type    = "authorization_code"))

token_data <- content(resp)
token      <- token_data$access_token

Your token should be a long string starting with "ya29.". If token is NULL, inspect content(resp) — an invalid_grant error means the code was already used or expired, so re-run browseURL(auth_url) to get a fresh one.

Step 5 - Open the IMAP connection

Pass the access token to configure_imap() via the xoauth2_bearer argument (no password):

library(mRpostman)

con <- configure_imap(
  url            = "imaps://imap.gmail.com",
  username       = "your_user@gmail.com",
  use_ssl        = TRUE,
  xoauth2_bearer = token
)

con$list_server_capabilities()

If this returns the server’s capabilities, you are authenticated and can use any mRpostman method (select_folder(), search_*(), fetch_*(), …).

Step 6 - Refresh the token

Access tokens are short-lived (about 1 hour). The refresh token returned in Step 4 (thanks to access_type = "offline") lets you mint a new access token without going through the browser again.

If you used Option A, httr refreshes automatically; you can also force it:

token_obj$refresh()
token <- token_obj$credentials$access_token

If you used Option B, exchange the stored refresh token directly:

refresh <- POST("https://oauth2.googleapis.com/token", encode = "form",
  body = list(client_id     = cred$client_id,
              client_secret = cred$client_secret,
              grant_type    = "refresh_token",
              refresh_token = token_data$refresh_token))

token <- content(refresh)$access_token

Then reopen (or update) the connection with the new token. To swap the token on an existing connection object, use con$reset_xoauth2_bearer(token).

Troubleshooting

  • access_denied / “hasn’t been verified by Google”: the account you logged in with is not in the consent screen’s Test users list (Step 2.4), or it is not the account that owns the project. Add it as a test user.
  • redirect_uri_mismatch: your OAuth client is a Web application, not a Desktop app. Recreate it as a Desktop app (Step 3).
  • createTcpServer: address already in use (Option A): the local callback port is busy — often a leftover server from a previous attempt in the same R session. Run httpuv::stopAllServers() and retry; if that fails, fully quit and reopen RStudio (a mere “Restart R” does not release the port), or switch to Option B, which needs no local server.
  • SASL error during authentication: your libcurl is likely older than 7.65.0. Update it and reinstall the curl R package (see the Introduction).
  • Other providers: replace the authorization/token endpoints and the scope with the provider’s values (for Google, oauth_endpoints("google") already fills them in), and set url accordingly, e.g. imaps://outlook.office365.com for Office 365.