Skip to main content

Google Auth & OAuth2

Most applications need user authentication. Google OAuth2 lets users sign in with their Google account, saving them from creating yet another password. Service accounts provide machine-to-machine authentication for accessing Google APIs (Drive, Sheets, Cloud Storage) programmatically.

OAuth2 Flow

OAuth2 allows your app to access a user's Google data without ever seeing their password:

code
User Your App Google
| | |
| Click | |
| "Sign in | |
| with | |
| Google" | |
|------------>| |
| | Redirect to |
| | Google login |
| |-------------->|
| | |
| User logs in & authorizes |
| |<--------------|
| | Auth code |
| | |
| | Exchange code |
| | for tokens |
| |-------------->|
| | Access token |
| | + ID token |
| |<--------------|
| | |
| User | |
| logged in | |
|<------------| |

Setting Up Google OAuth2

1. Create OAuth Credentials

  1. Go to Google Cloud Console
  2. Create a new project or select an existing one
  3. Navigate to APIs & Services → Credentials
  4. Click Create Credentials → OAuth client ID
  5. Configure:
    • Application type: Web application
    • Authorized JavaScript origins: http://localhost:3000
    • Authorized redirect URIs: http://localhost:3000/auth/callback
  6. Note the Client ID and Client Secret

2. Implement OAuth in FastAPI

python
# auth.py
from fastapi import FastAPI, HTTPException, Depends
from fastapi.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware
from authlib.integrations.starlette_client import OAuth
import os

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key=os.urandom(32).hex())

oauth = OAuth()
oauth.register(
name="google",
client_id=os.environ["GOOGLE_CLIENT_ID"],
client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
client_kwargs={"scope": "openid email profile"},
)

@app.get("/auth/login")
async def login(request):
redirect_uri = request.url_for("auth_callback")
return await oauth.google.authorize_redirect(request, redirect_uri)

@app.get("/auth/callback")
async def auth_callback(request):
token = await oauth.google.authorize_access_token(request)
user = token.get("userinfo")

if not user:
raise HTTPException(status_code=400, detail="Authentication failed")

# Store user info in session or create a JWT
return {
"email": user["email"],
"name": user["name"],
"picture": user["picture"],
}

@app.get("/auth/logout")
async def logout(request):
request.session.pop("user", None)
return RedirectResponse(url="/")
Use environment variables for secrets

Never hardcode GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET in your code. Store them in .env locally and configure them as environment variables in your deployment platform.

Service Accounts

Service accounts are for server-to-server communication. They let your application access Google APIs without user interaction.

Creating a Service Account

  1. In Google Cloud Console, go to IAM & Admin → Service Accounts
  2. Click Create Service Account
  3. Give it a name and description
  4. Grant the necessary roles (e.g., "Storage Object Viewer" for reading GCS)
  5. Click Create Key → JSON → Download the key file

Using Service Accounts

python
# service_account.py
from google.oauth2 import service_account
from google.cloud import storage
import json
import os

# Load credentials from environment variable (recommended)
credentials_info = json.loads(os.environ["GOOGLE_SERVICE_ACCOUNT_JSON"])
credentials = service_account.Credentials.from_service_account_info(
credentials_info,
scopes=["https://www.googleapis.com/auth/cloud-platform"],
)

# Use credentials with a Google Cloud client
storage_client = storage.Client(credentials=credentials)
bucket = storage_client.bucket("my-data-bucket")
blob = bucket.blob("data/latest_model.pkl")
blob.download_to_filename("model.pkl")

print("Model downloaded successfully")

Accessing Google Sheets

python
import gspread

# Authenticate with service account
gc = gspread.service_account_from_dict(credentials_info)

# Open a spreadsheet by key
spreadsheet = gc.open_by_key("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms")

# Read data from the first worksheet
worksheet = spreadsheet.sheet1
records = worksheet.get_all_records()

print(f"Found {len(records)} rows")
for row in records[:3]:
print(row)
Protect service account keys

Treat service account JSON keys like passwords. Never commit them to Git. In production, use Workload Identity (GKE) or Application Default Credentials instead of key files. On your local machine, set GOOGLE_APPLICATION_CREDENTIALS as an environment variable pointing to the key file path.

Token Validation

For API endpoints that require authentication, validate the Google ID token:

python
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests

async def get_current_user(authorization: str = Header(...)):
"""Validate Google ID token from Authorization header."""
try:
token = authorization.replace("Bearer ", "")
idinfo = id_token.verify_oauth2_token(
token,
google_requests.Request(),
os.environ["GOOGLE_CLIENT_ID"],
)
return {
"email": idinfo["email"],
"name": idinfo["name"],
"sub": idinfo["sub"],
}
except ValueError:
raise HTTPException(status_code=401, detail="Invalid token")

@app.get("/api/me")
async def get_me(user: dict = Depends(get_current_user)):
return user