Letting users sign in with their Google accounts can make your FastAPI app easier to use and more secure. Here's a clear, step-by-step guide to set it up.
Before jumping in, make sure you have:
A basic understanding of FastAPI.
A Google account to use the Google Developer Console.
A FastAPI project already set up.
You'll also need the httpx library for making HTTP requests. Install it with:
pip install httpx
First, you need to configure things in the Google Developer Console.
Head to the Google Developer Console.
Click "Select a project" at the top, then "New Project".
Name your project and hit "Create".
Go to "OAuth consent screen" in the sidebar.
Fill in the required fields like app name and user support email.
Under "Scopes", add the "email" scope. You can add others like "profile" if you want more data.
Save your changes.
Navigate to "Credentials".
Click "Create credentials" and pick "OAuth client ID".
Select "Web application".
Enter your app's name.
For "Authorized JavaScript origins", use your app's origin, like https://localhost:5000.
For "Authorized redirect URIs", add your callback URL, like https://localhost:5000/app/auth/google/callback.
Click "Create" and copy your Client ID and Client Secret.
Note: The redirect URI in your code must match this exactly, or it won't work.
Now, let's write the code to handle Google sign-ins in your FastAPI app.
You'll need these variables:
GOOGLE_CLIENT_ID: Your Client ID from Google.
GOOGLE_CLIENT_SECRET: Your Client Secret from Google.
HOST: Your app's base URL, like https://localhost:5000.
Set them in a .env file or your environment. For a .env file, it'd look like:
GOOGLE_CLIENT_ID=your_client_id GOOGLE_CLIENT_SECRET=your_client_secret HOST=https://localhost:5000
Here's the FastAPI code to handle the login and callback:
from fastapi import APIRouter, HTTPException, Request, status from fastapi.responses import RedirectResponse import httpx router = APIRouter(prefix="/app") # Replace these with your actual values or load from environment GOOGLE_CLIENT_ID = "your_client_id" GOOGLE_CLIENT_SECRET = "your_client_secret" HOST = "https://localhost:5000" GOOGLE_REDIRECT_URI = f"{HOST}/app/auth/google/callback" GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth" GOOGLE_TOKEN_URL = "https://accounts.google.com/o/oauth2/token" GOOGLE_USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo" GOOGLE_SCOPE = "email" @router.get("/auth/google/login", include_in_schema=False, response_class=RedirectResponse) async def login_google(): """Redirect the user to Google's login page.""" google_oauth_url = ( f"{GOOGLE_AUTH_URL}?response_type=code&client_id={GOOGLE_CLIENT_ID}" f"&redirect_uri={GOOGLE_REDIRECT_URI}&scope={GOOGLE_SCOPE}&access_type=offline" ) return RedirectResponse(google_oauth_url, status_code=status.HTTP_302_FOUND) @router.get("/auth/google/callback", include_in_schema=False) async def auth_google(request: Request): """Handle the callback from Google after login.""" code = request.query_params.get("code") if not code: raise HTTPException(status_code=400, detail="No authorization code found") # Swap the code for an access token data = { "code": code, "client_id": GOOGLE_CLIENT_ID, "client_secret": GOOGLE_CLIENT_SECRET, "redirect_uri": GOOGLE_REDIRECT_URI, "grant_type": "authorization_code", } async with httpx.AsyncClient() as client: response = await client.post(GOOGLE_TOKEN_URL, data=data) response_data = response.json() access_token = response_data.get("access_token") if not access_token: raise HTTPException(status_code=400, detail="No access token received") # Get user info with the access token async with httpx.AsyncClient() as client: user_info_response = await client.get( GOOGLE_USER_INFO_URL, headers={"Authorization": f"Bearer {access_token}"} ) user_info = user_info_response.json() # Add your user handling logic here (e.g., create or log in user) return user_info # For now, just return the info
Login Route: Sends the user to Google's login page with your app's details.
Callback Route: Takes the code Google sends back, trades it for an access token, and uses that to grab the user's info (like their email).
Right now, the callback just returns the user info. You'll want to add logic to check if the user exists in your database. If not, create a new user with the Google ID and email. If they do exist, log them in. You could also set a cookie or token for session management.
Here's the flow after clicking "Sign in with Google":
You're sent to Google's sign-in page.
Google checks your credentials and sends an authorization code to your app.
Your app sends that code back to Google to get an access token.
The access token lets your app fetch the user's info.
Your query mentioned saving the Google ID in the database, which is a good idea. The code you provided does that, but I've simplified it here to focus on the basics. If you want to use the full version with IP tracking and custom models (like UserInfoResponse), you'll need to define those separately. For example, UserInfoResponse might look like:
from pydantic import BaseModel class UserInfoResponse(BaseModel): id: str email: str verified_email: bool picture: str
Also, your approach is solid, but I'd suggest adding a state parameter to the login URL to prevent CSRF attacks. Google supports it, and it's a simple security boost.
That's it! You've added Google Authentication to your FastAPI app. Users can sign in with their Google accounts, and you can use their info as needed. Test it out, and tweak it to fit your app - like adding more scopes or better error handling. Keep the access token secure, and you're good to go.
Enjoyed this blog post? Check out these related posts!
Adding middleware to FastAPI Applications: Process Time Headers, Security, and Compression
A practical guide to implementing middleware in FastAPI for better performance, security, and efficiency.
Read More..
Full-Text Search: Using the Trigram Tokenizer Algorithm to Match Peoples Names
Leveraging Full Text Search and Trigram Tokenization for Efficient Name Matching
Read More..
Deploying Reflex Front-End with Caddy in Docker
A step-by-step guide to building and serving Reflex static front-end files using Caddy in a Docker container
Read More..
Reflex Makes SEO Easier: Automatic robots.txt and sitemap.xml Generation
Discover how adding your deploy URL in Reflex automatically generates robots.txt and sitemap.xml for easier SEO.
Read More..
Have a project in mind? Send me an email at hello@davidmuraya.com and let's bring your ideas to life. I am always available for exciting discussions.