Google API Authentication for AI Applications: A Practical Guide to OAuth and Credential Management
Building an AI agent that touches Gmail, Calendar, or Drive means solving Google API authentication for every user, every day. This guide covers OAuth 2.0 setup, credentials, scopes, and token management for AI applications at scale.
Ask an AI agent to check a calendar, draft an email, or read a spreadsheet, and the hardest part of that request is rarely the AI itself. It is proving to Google, on every single call, that the person behind the request actually agreed to it. Google API authentication is not a setting configured once and forgotten. It is an ongoing system of credentials, scopes, and tokens that has to keep working correctly for every user, every day, long after the original login screen has closed.
Most engineering teams learn this the slow way. A prototype works fine with one developer's Google account and a single OAuth client. Then real users show up, tokens start expiring at different times, a refresh token quietly stops working after a week in testing, and a scope that seemed harmless in a demo turns out to need a security review before anyone else can use it.
This guide covers Google API authentication for AI applications end to end: setting up Google OAuth 2.0 correctly from the start, managing Google API credentials across several services at once, choosing Google API scopes and permissions that match the principle of least privilege, keeping Google API access tokens and Google OAuth refresh tokens alive without surprising a user, and handling the authentication failures that show up no matter how carefully everything was built.
Authenticating AI Applications with Google Services
Every AI application that needs to read a Gmail inbox, update a Google Calendar, or pull rows from a spreadsheet runs into the same wall eventually. Google will not hand over that data just because a request looks legitimate. It needs proof that a real user agreed to the request, in a form it can verify on every call.
For a general purpose API call, such as asking the Gemini API to generate text or calling the Maps API for a static lookup, an API key or a service account is usually enough. Neither of those touches a specific person's private data, so Google does not require a human to click "allow" first.
The moment an AI agent needs to act as a specific user, reading that user's email, writing to their calendar, editing their spreadsheet, the requirement changes. Google needs a signal that the account owner authorized this exact access, and that signal is Google OAuth 2.0.
This is where AI agents complicate a pattern that has existed for years. A traditional web app authenticates one user at a time, during a session, with a person at a keyboard to approve it. An AI agent often needs to:
- Hold valid credentials for hundreds or thousands of different users at once, not just one
- Keep working long after the user who granted access has closed the tab
- Touch several Google APIs inside a single task, Gmail to draft a message, Calendar to check for a conflict, Drive to attach a file, each governed by its own scope
- Do all of this without a person present to click through a new login if something breaks
Three approaches typically apply, and choosing the wrong one for the job is where most of the pain starts:
- API keys: suitable for public data or calls that are not tied to an individual account, not sufficient for anything that touches a specific person's information
- Google OAuth 2.0: the standard for acting on behalf of a specific Google user, requires that user to grant consent once, after which the app holds tokens scoped to what they approved
- Service accounts with domain wide delegation: suited to Google Workspace environments where an administrator grants an app standing access across the organization, skipping a per user consent screen entirely
Whether the agent calls a Google API directly or reaches it through an MCP server, the OAuth foundation underneath does not change, which is part of why getting this right early pays off no matter how the integration layer above it evolves.
If authentication is the first Google integration your team has built, it helps to start with how Google APIs are structured across products like Maps, Gmail, Drive, and Calendar before going deeper into OAuth specifically. The rest of this guide assumes that context and focuses on getting Google API authentication right for an AI application operating at scale.
Managing OAuth Credentials Across Multiple Google APIs
Every Google API a project uses, Gmail, Calendar, Drive, Sheets, or dozens of others, is unlocked through the same underlying credential set, but reaching that point takes a few required steps inside Google Cloud Console.
- Create or select a Google Cloud project: this is the container for enabled APIs, credentials, quota, and billing
- Enable each API individually: the Gmail API, Calendar API, Drive API, and Sheets API are enabled separately even within the same project, and a call will fail if its API has not been turned on
- Configure Google Auth Platform: this is what Google Cloud Console called the OAuth consent screen before reorganizing it into Branding, Audience, Data Access, and Clients tabs, and it is where the app name, support email, scopes, and user type (Internal or External) are set
- Create an OAuth client ID and secret under the Clients tab, choosing the correct client type (Web application, Desktop app, iOS, Android, and so on), since each type carries different security requirements
- Register redirect URIs exactly as the app will use them: Google matches these as an exact string, so a trailing slash or a mismatched port produces a redirect_uri_mismatch error
Once these credentials exist, an AI application calling several Google APIs on behalf of many users faces a second question: how to structure and store all of it.
For a single Google Cloud project used across Gmail, Calendar, Drive, and Sheets, one client ID and secret typically cover every API, since the credential authenticates the application itself, not the individual API. What changes per user is the OAuth token, generated after that user completes consent and scoped to whatever combination of APIs and permissions they approved.
For connecting Drive, Calendar, and Sheets to an AI agent specifically, the practical difference between these APIs mostly shows up in scope design and rate limits rather than in how credentials are issued.
The bigger challenge for AI applications is storage, not issuance. A production agent serving many tenants ends up holding:
- A client ID and secret per Google Cloud project, rarely more than one or two per environment
- An access token and refresh token per user, per set of granted scopes, which can mean thousands of token pairs for a growing product
A few practices for storing this safely:
- Never hardcode client secrets or tokens in source code, and never commit them to a repository
- Store secrets in environment variables for small deployments, or a dedicated secret manager (Google Secret Manager, HashiCorp Vault, AWS Secrets Manager) for production systems
- Encrypt stored refresh tokens at rest, since a leaked token is effectively standing access to a user's Google account until it is revoked
- Separate development and production credentials into different Google Cloud projects, so a bug in a test environment cannot touch real user data
This is exactly the layer of plumbing that grows unmanageable by hand once a product supports more than a handful of tenants and more than one or two integrations.
Managing API Scopes and Permissions Across Google Services
Scopes are the actual permission strings attached to an OAuth token, and they matter more than almost any other decision in this process, since they define exactly what a granted token can and cannot touch.
Google groups every scope into one of three tiers:
- Basic scopes: profile information, email address, or openid, these carry no special review requirement and are not considered sensitive
- Sensitive scopes: broader access such as gmail.send or calendar.events, these require Google to review and approve the app before external users can grant them
- Restricted scopes: the widest access, such as full read and write access to Gmail, these require both a Google verification review and a separate third party security assessment, often called CASA, repeated annually for apps that store this data outside Google's own systems
A short list of scopes commonly used by AI agents:
- gmail.readonly: read only access to Gmail messages
- gmail.send: permission to send email, without read access
- calendar.events: create and modify calendar events
- drive.file: access limited to files the app itself created or that a user explicitly opened with it
- drive.readonly: broader read only access across a user's Drive
- spreadsheets: read and write access to Sheets
The principle worth following here is least privilege. drive.file, for example, only exposes files the app created or that the user picked through a file picker, while the general drive scope exposes everything in that user's Drive. For most AI agents built around a specific workflow, drafting documents or summarizing a folder the user selected, the narrower scope is both safer and easier to get verified.
Incremental authorization follows the same logic. Rather than requesting every scope an app might ever need at sign up, request scopes as features are actually used: ask for Calendar access when a user first tries to schedule something through the agent, rather than bundling it into an initial screen the user has to accept blindly.
The tradeoff is real though. Every sensitive or restricted scope added to an app increases what Google's verification team asks for, a written justification for each scope, and for restricted scopes, a demo video showing exactly how the data gets used. Broader scopes also mean broader damage if a token is ever compromised, so it is worth checking whether a scope built for that exact purpose already covers what the agent needs before defaulting to the widest option available.
Google OAuth 2.0 Best Practices for AI Applications
Getting a flow working is the easy part. Running it reliably for months, across many users and several Google APIs, is where most of the actual engineering work lives. A few practices consistently separate AI applications that hold up in production from ones that quietly break for a subset of users.
Use the authorization code flow with PKCE for anything that is not a pure server side web app. Google now requires PKCE for Desktop, iOS, Android, and Chrome extension client types, and using it even where not strictly required closes off a class of interception attacks against the authorization code.
Store one token set per tenant, never a single shared credential across users. This sounds obvious until an AI product gets built quickly with one developer's own Google account for testing, and that pattern quietly survives into production. Every user needs their own OAuth grant, their own tokens, and their own scopes, tracked separately.
Keep development and production in separate Google Cloud projects. This matters even more for scopes than for general safety: a new sensitive or restricted scope can be tested freely in a project still in Testing status, without that unapproved scope ever reaching real users in production before Google finishes reviewing it.
Treat prompts and logs as a leak surface. This one is specific to AI applications: a client secret or access token that ends up inside a prompt, a model's context window, or a debug log can surface again somewhere unexpected, a transcript, an error message shown to a user, or a stored conversation. Credentials belong in a secrets layer the model never sees directly, with only the minimum needed to make a call passed through at execution time.
Build a visible permission layer for what the agent is about to do, particularly before destructive or hard to reverse actions like sending an email or deleting a file. A user who granted gmail.send access still benefits from seeing the actual message before it goes out, especially the first several times they use a new agent.
These patterns extend past Google specifically. The broader guide to multi tenant OAuth for AI agents covers how to structure credential storage and scope handling once an application juggles more than one provider, not just Google's APIs.
Handling Google OAuth Access and Refresh Token Expiration
Two different tokens do two different jobs, and confusing them is a common source of bugs in early AI agent builds.
One detail trips up more developers than anything else in this guide: Google only issues a refresh token the first time a user authorizes an app, unless the request explicitly asks for one again. To guarantee getting a refresh token, even for a user who has already granted access before, the authorization request needs two specific parameters:
https://accounts.google.com/o/oauth2/v2/auth?
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REDIRECT_URI&
response_type=code&
scope=SPACE_SEPARATED_SCOPES&
access_type=offline&
prompt=consent
access_type=offline tells Google this app needs ongoing access, not just access for the current session. prompt=consent forces the consent screen to appear and issue a fresh refresh token even if the user technically approved this app before.
With that covered, here is what each token actually does:
Access tokens: short lived, typically valid for one hour (3600 seconds), sent with every API call to prove the request is authorized right now. When it expires, the API responds with a 401 and the token simply needs replacing, not a whole new authorization flow.
Refresh tokens: longer lived, used to obtain a new access token without asking the user to sign in again. They are not permanent though, and several conditions can end one early:
- The app's Google Auth Platform publishing status is still set to Testing, in which case Google expires every refresh token after 7 days, regardless of how often it gets used, unless the only scopes requested are basic profile information
- The user manually revokes the app's access from their Google Account
- The refresh token goes unused for 6 consecutive months
- The user changes their password, which can revoke tokens tied to Gmail scopes specifically
- A Google Workspace administrator restricts a scope the token relied on
- More than 100 live refresh tokens accumulate for the same user and client combination, at which point Google silently invalidates the oldest one
A minimal token refresh request looks like this:
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
refresh_token=USER_REFRESH_TOKEN&
grant_type=refresh_token
A successful response returns a new access token and its expiry, without issuing a new refresh token in most cases, so the original refresh token stays in storage and gets reused for the next refresh.
The pattern that holds up well in production: refresh proactively, a few minutes before the current access token expires, rather than waiting for the first request to fail with a 401. This avoids a race where several parts of an application discover an expired token at the same time and each tries to refresh it independently.
An invalid_grant response to a refresh request is different from a normal expiry, and should be handled differently. It usually means the refresh token itself is gone, revoked, expired out of the Testing window, or invalidated by one of the conditions above, and retrying the same request will not fix it. That case needs the user to reauthorize, not a retry loop.
Securing and Rotating Google API Credentials
A refresh token is effectively a long lived password to a piece of a user's Google account, and it deserves the same handling a password would get, not the casual treatment an API key sometimes receives.
A few concrete practices:
- Rotate client secrets on a schedule, not only after a suspected leak. Google allows more than one active secret per OAuth client during a rotation window, so a new secret can go live in production before the old one is deleted, avoiding downtime
- Store tokens encrypted at rest, never as plain text in a database column or a log line. If a database backup or a log export ever leaks, encrypted tokens limit the damage to something that still needs a key to use
- Treat any leaked secret or token as compromised immediately, not just probably compromised. Revoke it through Google's token revocation endpoint or the Google Auth Platform Clients page, then issue a new one, rather than waiting to confirm actual misuse first
- Watch for accidental exposure in public repositories. GitHub's own secret scanning catches many Google client secrets automatically, but a private fork, a pasted log in a support ticket, or a screenshot in a shared document will not be caught by that
- Review granted access periodically, both from the user's side (Google Account, Security, Third party access) and from an admin's side for Workspace deployments. Unused grants sit quietly on the account as a liability even when nothing has gone wrong yet
- Revoke tokens programmatically the moment a user disconnects an integration inside your product, rather than only deleting the local copy. A token that still exists on Google's side, even if your app has forgotten about it, remains valid until Google is told to revoke it
The idea underneath all of this: a credential that grants standing access to real user data should be as hard to misuse as possible, and rotation, encryption, and prompt revocation are the three levers that actually move that needle.
Managing Authentication Failures and Reauthorization
Even a well built integration hits authentication failures eventually. A token expires at an inconvenient time, a user revokes access without telling anyone, or Google changes a verification requirement. What separates a resilient AI application from a fragile one is how it responds when that happens.
Here is what the most common failures actually mean, and how to respond to each:. a 401 Unauthorized typically means the access token has expired or is malformed, and the fix is to refresh the access token and retry the same request once. An invalid_grant error means the refresh token itself has expired, been revoked, or been invalidated, and the correct response is to stop refreshing and prompt that specific user to reauthorize. A 403 insufficient scope error means the token does not include a scope the request needs, which should trigger incremental authorization for the missing scope. A redirect_uri_mismatch means the registered redirect URI does not exactly match the request, and the fix is to correct the registered URI in Google Auth Platform rather than changing app code to guess at it. An unverified app warning means the app is requesting sensitive or restricted scopes without having completed verification, which requires either completing Google's verification process or reducing scopes for testing. A reauthorization flow that works well in practice:
- Detect the failure at the point of the API call, not several steps downstream where the original cause gets harder to trace
- Mark that specific user's connection as needing reauthorization, rather than treating it as a global outage affecting everyone
- Prompt only that user the next time they interact with the product, with a clear reason (their Google connection needs to be renewed) rather than a generic error
- Send them back through the consent screen requesting the same scopes they had before, so they are not surprised by a broader ask
Logging matters here too. Auth failures should be logged with enough context to debug, which tenant, which scope, which API, which error code, without ever logging the token or secret value itself. A log line that includes a live refresh token turns a routine debugging session into a credential leak.
Much of this maintenance load compounds as integrations grow, which is why manual OAuth setup increasingly slows down workflow automation once a product depends on more than one or two Google APIs alongside everything else it connects to.
None of this is unique to Google. The same pattern, credential storage, scope design, token refresh, rotation, and reauthorization, repeats for Slack, GitHub, Notion, and every other API an AI agent might need to call, each with its own quirks layered on top. Corsair is an open source integration layer built to handle exactly this plumbing, OAuth flows, encrypted credential storage, scope management, and multi tenant isolation, for Google's APIs and hundreds of others, so a team can spend its engineering time on what the agent actually does rather than rebuilding this same authentication layer for every new integration.