I'm going to be honest β I'm still figuring out the authentication flow in my Next.js app. Right now, I'm using Google OAuth for a Calendar integration, and every time I think I've nailed itβ¦ a token expires and my whole "flawless" login flow collapses.
If you've been down this road, you already know the fun parts:
- Cookies disappearing like socks in the dryer
- Google's
invalid_grant
error appearing out of nowhere - That feeling when your API route works only if you log in 5 seconds ago
This post isn't "Here's the perfect authentication guide." It's more like "Here's what I'm doing right now so my app works β but I know there are better ways."
The messy reality of OAuth
Most OAuth tutorials basically stop after:
- Click "Sign in with Google"
- Get
access_token
- Call an API π
That's fine for a demo. In real life:
- Tokens expire (sometimes faster than you expect)
- Refresh tokens aren't always provided
- You have to decide where to handle expiry β server, client, or both
Spoiler: I learned you actually need both.
My current API route (work in progress)
Here's the route I'm using to fetch Google Calendar events. It's not perfect, but it's keeping my app from falling apart (most of the time).
// /pages/api/eventList.js
import { google } from "googleapis";
import oauth2Client from "@/utils/google-auth";
export default async function handler(req, res) {
function getFirstDayOfLastMonth() {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString();
}
const {
google_access_token,
expiry_date,
refresh_token_expires_in,
refresh_token,
} = req.cookies;
if (req.method === "GET") {
try {
oauth2Client.setCredentials({
access_token: google_access_token,
expiry_date,
refresh_token_expires_in,
refresh_token,
});
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
const { data } = await calendar.events.list({
auth: oauth2Client,
calendarId: "primary",
// maxResults:13,
timeMin: getFirstDayOfLastMonth()
// timeMin: "2025-07-01T00:00:00.000Z",
});
res.status(200).json({ events: data.items });
} catch (error) {
console.error("Google API error:", error);
// Normalize error
let normalizedError = "Unknown error";
if (error?.response?.data?.error) {
normalizedError = error.response.data.error;
} else if (error?.errors?.length) {
normalizedError = error.errors[0].message;
} else if (error?.message) {
normalizedError = error.message;
}
res.status(400).json({ error: normalizedError, code: error?.code || null });
}
} else {
res.setHeader("Allow", ["GET"]);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
The good:
- If there's no token, I stop immediately.
- If Google says the token is bad, I return a
401
.
The "still figuring out" parts:
- I don't refresh tokens automatically yet.
- I don't update cookies when I do get a new token.
- My error handling works, but it's not super descriptive.
Client-side "bail out" strategy
Right now, my frontend just looks for 401
and punts the user back to /login
.
async function fetchEvents() {
const res = await fetch("/api/eventList");
if (res.status === 401) {
localStorage.setItem("loggedOutDueToTokenIssue", "true");
window.location.href = "/login";
return;
}
return res.json();
}
It's simple, and it works⦠but it also means the user gets kicked out even in cases where I could have refreshed the token in the background.
Things I know I need to improve
β
Automatic token refresh on the server (before hitting Google's API)
β
Updating cookies when I refresh a token
β
Returning more structured error messages so the client knows exactly what happened
β
Maybe centralizing all token logic so I don't repeat it in every route
Final thoughts (for now)
I'm sharing this because sometimes dev posts make it sound like people go from "I want OAuth" to "I have the perfect auth system" in one weekend. The truth is, I'm still tripping over my own code, but each small improvement makes the whole thing less fragile.
If you've built a rock-solid Google OAuth flow in Next.js β especially one that gracefully handles token refresh β please drop your tips in the comments. I could really use them. π
Top comments (0)