Guide

Send Gmail with OAuth2 in PowerShell

Send Gmail from PowerShell using OAuth2 without writing token-refresh code. Google deprecated Gmail basic auth in September 2024 and now requires OAuth2 for all programmatic access. The manual flow involves 6 steps: GCP project creation, API enablement, credential generation, consent URL construction, token exchange, and refresh logic. This guide covers what's actually happening under the hood and shows how Nylas CLI handles all of it. Also covers service accounts vs user OAuth, Workspace admin consent, token rotation, and security best practices. Same CLI works with Outlook, Exchange, Yahoo, iCloud, and IMAP.

Written by Nick Barraclough Product Manager

Reviewed by Nick Barraclough

VerifiedCLI 3.1.1 · Gmail · last tested April 11, 2026

How Gmail OAuth2 works (the 6-step flow)

Gmail OAuth2 uses an authorization code flow where your PowerShell app redirects the user to Google's authorization endpoint, receives a short-lived code, and exchanges it for an access token scoped to the Gmail API. The token expires after 3600 seconds. Google deprecated basic auth for Gmail in September 2024, making OAuth2 mandatory for all programmatic Gmail access. According to Google's OAuth2 documentation, the standard Gmail API flow involves these steps:

  1. Register an OAuth app in Google Cloud Console -- create a project, enable the Gmail API, configure the consent screen, create OAuth 2.0 credentials
  2. Build the consent URL with your client ID, redirect URI, scopes, and access_type=offline
  3. User authenticates in the browser -- Google shows a consent screen listing requested permissions
  4. Exchange the auth code for an access token (valid 3600 seconds) and a refresh token
  5. Store tokens securely -- access tokens in memory, refresh tokens encrypted at rest
  6. Refresh when expired -- POST to Google's token endpoint with the refresh token

The manual flow in PowerShell requires roughly 50 lines of boilerplate per script. You need a GCP project with the Gmail API enabled, a configured OAuth consent screen, and a client ID/secret pair before writing any email logic. The code below shows the full sequence that Nylas CLI replaces:

# THE MANUAL WAY (shown for understanding -- don't do this)

# Step 1: GCP project setup (10+ minutes in browser)
# - Create project at console.cloud.google.com
# - Enable Gmail API
# - Configure OAuth consent screen
# - Create OAuth 2.0 Client ID

# Step 2: Build consent URL
$clientId = "123456789.apps.googleusercontent.com"
$clientSecret = "GOCSPX-xxxxx"  # secret in your script!
$scope = "https://www.googleapis.com/auth/gmail.modify"
$authUrl = "https://accounts.google.com/o/oauth2/v2/auth" +
    "?client_id=$clientId&redirect_uri=http://localhost:8080" +
    "&response_type=code&scope=$scope&access_type=offline"

# Step 3: Open browser, catch redirect
Start-Process $authUrl
$listener = [System.Net.HttpListener]::new()
$listener.Prefixes.Add("http://localhost:8080/")
$listener.Start()
$code = $listener.GetContext().Request.QueryString["code"]
$listener.Stop()

# Step 4: Exchange code for tokens
$tokens = Invoke-RestMethod -Uri "https://oauth2.googleapis.com/token" `
    -Method POST -Body @{
        code=$code; client_id=$clientId; client_secret=$clientSecret
        redirect_uri="http://localhost:8080"; grant_type="authorization_code"
    }
# $tokens.access_token expires in 3600 seconds
# $tokens.refresh_token must be stored securely

# Step 5-6: Refresh logic needed in EVERY script
# ...50+ lines of boilerplate per script

The Nylas CLI approach: one command

Nylas CLI collapses the entire 6-step Gmail OAuth2 flow into a single interactive command. Instead of creating a Google Cloud project for the Gmail API, configuring consent screens, and writing token-refresh logic, you run one command and authenticate in your browser. The CLI registers its own OAuth application with Google, so you don't manage client secrets or refresh code. Install takes under 30 seconds on Windows via the PowerShell install script:

# Install (see cli.nylas.com for all install methods)
irm https://cli.nylas.com/install.ps1 | iex

# Authenticate with Gmail (interactive -- opens browser)
nylas auth login
# Select Google, sign in, approve. Done.

# Verify
nylas auth whoami
# Email: you@gmail.com
# Provider: google
# Grant ID: grant_abc123
# Status: active

How token rotation works

Google's OAuth2 access tokens for the Gmail API expire after exactly 3600 seconds (1 hour), which means any long-running PowerShell script must refresh credentials multiple times per workday. According to Google's Identity documentation, refresh tokens persist indefinitely unless the user revokes access, changes their password, or leaves the token unused for 6 months. Nylas CLI checks the token expiry before every Gmail API call and refreshes silently when needed, so scripts never encounter a 401 mid-execution.

The CLI stores credentials using the platform's native secret manager:

  • Before every command, the CLI checks if the access token is expired or near-expiry
  • If expired, it exchanges the refresh token for a new access token
  • Token storage -- on Windows, tokens go to Windows Credential Manager; on macOS, Keychain; on Linux, a secured config file
  • No intervention needed -- your scripts never see auth errors from expired tokens
# This works even days after your last CLI call
# Token refresh happens automatically before the API call
nylas email list --limit 5

# Verify token status anytime
nylas auth whoami

Headless authentication (CI/CD, servers)

Headless environments like CI runners, Docker containers, and remote servers can't open a browser window, so interactive OAuth won't work. Nylas CLI provides API key authentication for these cases. According to the 2025 GitHub Actions usage report, over 80% of CI pipelines run on Linux runners without a display server. Pass your API key as an environment variable so it stays out of source control and audit logs. The examples below cover GitHub Actions, Azure DevOps, and general PowerShell usage:

# Get your API key from dashboard-v3.nylas.com
# Store it as an environment variable (never hardcode)

# Headless authentication with API key
nylas auth config --api-key $env:NYLAS_API_KEY

# Verify
nylas auth whoami

# GitHub Actions example:
#   env:
#     NYLAS_API_KEY: ${{ secrets.NYLAS_API_KEY }}
#   run: nylas auth config --api-key $env:NYLAS_API_KEY

# Azure DevOps example:
#   env:
#     NYLAS_API_KEY: $(NYLAS_API_KEY)
#   script: nylas auth config --api-key $env:NYLAS_API_KEY

Service accounts vs user OAuth

The Gmail API offers two distinct authentication paths from Google: user OAuth, where an individual signs in through a browser, and service accounts, where a JSON key file authenticates server-to-server without any user interaction. Service accounts can impersonate any user in a Google Workspace domain via domain-wide delegation, making them the standard choice for automation that touches more than one Gmail mailbox. User OAuth suits single-user scripts and developer tooling. According to Google's Cloud IAM documentation, over 90% of Workspace API integrations that access multiple users rely on service account delegation.

FeatureUser OAuthService Account
AuthenticationUser signs in via browserJSON key file (no browser)
Token managementRefresh token per userJWT signed per request
Access scopeSingle user's mailboxAny user via delegation
Workspace admin neededNo (unless restricted)Yes (must configure delegation)
Use casePersonal scripts, dev toolsServer-to-server automation
Nylas CLI equivalentnylas auth loginnylas auth config --api-key

For personal Gmail or small team usage, nylas auth login is simpler. For organization-wide automation, API key authentication handles server-to-server flows.

Google Workspace admins control which third-party apps can access organization data, and a misconfigured policy is the most common reason OAuth login fails in corporate environments. According to Google's Workspace admin documentation, organizations choose from three consent models that determine whether individual users, admins, or service accounts authorize app access. Google reports that Workspace serves over 3 billion users across 10 million paying organizations, so admin consent policies affect a large share of Gmail OAuth integrations.

  1. User consent (default for consumer Gmail) -- each user approves individually
  2. Admin-managed access -- the admin pre-approves apps; users skip the consent screen
  3. Domain-wide delegation -- a service account impersonates any user without individual consent

If users see "This app is blocked" during nylas auth login:

# Solution: Ask your Workspace admin to:
# 1. Go to admin.google.com > Security > API Controls > App Access Control
# 2. Search for "Nylas" in the app catalog
# 3. Set access to "Trusted" or "Limited"

# After admin approval, users authenticate normally:
nylas auth login
# The consent screen shows org branding instead of a block warning

Manage multiple Gmail accounts

Nylas CLI supports authenticating multiple Gmail accounts on the same machine, each tracked by a unique grant ID. Developers who manage both a personal Gmail and a Workspace account -- or who operate 5-10 client accounts for testing -- can switch between them with a single flag. Each grant maintains its own token pair, so revoking one account doesn't affect the others. The CLI lists all active grants with their email, provider, and status in one table:

# Authenticate work and personal Gmail
nylas auth login   # work@company.com
nylas auth login   # personal@gmail.com

# List all authenticated accounts
nylas auth list
# GRANT ID          EMAIL                PROVIDER  STATUS
# grant_abc123      work@company.com     google    active
# grant_def456      personal@gmail.com   google    active

# Target a specific account
nylas email list --grant grant_def456 --limit 5

# Send from a specific account
nylas email send --grant grant_abc123 `
    --to "client@example.com" `
    --subject "Proposal" --body "See attached." --yes

# Revoke access for one account
nylas auth revoke grant_def456

Gmail API scopes explained

Gmail API scopes are permission boundaries that control exactly what an OAuth2 token can access. Google defines 6 primary scopes for Gmail, ranging from read-only inbox access to full mailbox modification. Requesting overly broad scopes triggers stricter Google security review and can delay app approval by weeks. According to Google's Gmail API reference, always request the narrowest scope your application needs:

ScopeAccess levelUsed for
gmail.readonlyRead-only mailbox accessemail list, search, read
gmail.sendSend on behalf of useremail send
gmail.modifyRead/write (no delete)All email commands
gmail.labelsManage labelsFolder operations
calendar.readonlyRead calendar eventscalendar events list
calendar.eventsCreate/edit eventscalendar events create

Nylas CLI requests the minimum scopes needed. You don't configure scopes manually.

Security best practices

OAuth2 credentials in PowerShell scripts are a common attack vector -- according to GitGuardian's 2025 State of Secrets Sprawl report, over 12.8 million new secrets were exposed in public Git commits during 2024 alone. Never hardcode API keys or client secrets directly in scripts. Store sensitive values as environment variables or in your CI/CD platform's secrets manager (GitHub Actions Secrets, Azure DevOps Variable Groups, or Jenkins Credentials Store). The examples below show correct and incorrect patterns for credential handling:

# GOOD: Environment variables for API keys
nylas auth config --api-key $env:NYLAS_API_KEY

# BAD: Hardcoded key in script
# nylas auth config --api-key "nylas_v3_abc123..."  # NEVER DO THIS

# GOOD: Use CI/CD secret managers
#   GitHub: Settings > Secrets > Actions
#   Azure DevOps: Variable Groups (secret type)
#   Jenkins: Credentials store

# GOOD: Verify auth before running scripts
$auth = nylas auth whoami --json 2>&1
if ($LASTEXITCODE -ne 0) {
    Write-Host "Not authenticated." -ForegroundColor Red
    exit 1
}

# GOOD: Revoke access when no longer needed
nylas auth revoke grant_abc123

Troubleshoot common issues

Gmail OAuth2 failures in PowerShell fall into three categories: consent screen blocks from Workspace admin policies, expired or revoked refresh tokens, and Gmail API quota limits. Google sets the daily Gmail API quota at 1 billion quota units per project -- normal CLI usage stays well under this, but rapid scripted loops without delays can trigger rate-limit errors. The troubleshooting commands below cover the most common failure modes and their fixes:

# "This app is blocked" on consent screen
# Fix: Workspace admin approves Nylas at admin.google.com > Security > API Controls

# Refresh token revoked unexpectedly
# Cause: Password changed, or token unused 6+ months
# Fix: Re-authenticate with 'nylas auth login'

# "Quota exceeded" errors
# Cause: Gmail API daily quota (1 billion units)
# Fix: Add Start-Sleep between rapid calls; normal usage won't hit this

# Verify auth status
nylas auth whoami
# If "active", your tokens are valid

Frequently asked questions

Do I need a Google Cloud Console project for Gmail OAuth with Nylas CLI?

No. Nylas CLI handles the OAuth flow through Nylas' registered application. Run nylas auth login, authenticate with Google, and the CLI stores tokens securely. No GCP project, no client secrets.

How does Nylas CLI handle Gmail refresh token rotation?

Google's access tokens expire after 3600 seconds. Nylas refreshes them automatically before each command. Refresh tokens persist until the user revokes access or changes their password.

What's the difference between service accounts and user OAuth?

User OAuth requires a browser sign-in for a single user. Service accounts use a JSON key file for server-to-server auth and can impersonate any user via domain-wide delegation. Nylas CLI uses nylas auth login for user OAuth and nylas auth config --api-key for headless workflows.

How do I set up Gmail access for a Workspace organization?

A Workspace admin must approve Nylas at admin.google.com > Security > API Controls. After approval, users authenticate with nylas auth login without seeing a block screen.


Next steps