Add Google OAuth to a TypeScript application
This note replaces an earlier AI-chat transcript. Its client-side example used gapi.auth2, which is deprecated: Google’s migration guide says the Google Sign-In JavaScript platform library (apis.google.com/js/platform.js, api.js, gapi.auth2) is deprecated and replaced by Google Identity Services (GIS), loaded from https://accounts.google.com/gsi/client.
Choose the flow
| Need | Use |
|---|---|
| ”Sign in with Google” (identify the user) | GIS google.accounts.id (button, One Tap) in the browser, then verify the ID token on your server |
| Call Google APIs for the user from a backend (offline access) | Server-side authorization-code flow with google-auth-library |
| Call Google APIs from the browser only | GIS token client google.accounts.oauth2 |
Server-side authorization-code flow (Node + TypeScript)
Steps from Google’s web-server OAuth guide: create a client ID and secret in Google Cloud Console, register the redirect URI exactly (HTTPS required except for localhost), redirect the user with response_type=code, scope, a state value against CSRF, and access_type=offline if you need a refresh token; then exchange the returned code for tokens and store them securely.
npm install google-auth-library express
npm install -D typescript @types/express @types/node
npx tsc --init import express from 'express';
import { OAuth2Client } from 'google-auth-library';
const app = express();
const client = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI,
);
app.get('/auth/google', (_req, res) => {
const url = client.generateAuthUrl({
access_type: 'offline',
scope: ['openid', 'email', 'profile'],
state: 'generate-and-store-a-random-value-per-session', // verify on callback
});
res.redirect(url);
});
app.get('/auth/google/callback', async (req, res) => {
const code = req.query.code;
if (typeof code !== 'string') return res.status(400).send('Missing code');
const { tokens } = await client.getToken(code);
client.setCredentials(tokens);
// store tokens server-side; never echo them to the browser
res.send('Signed in');
});
app.listen(process.env.PORT ?? 3000); Browser sign-in with GIS, verify on the server
Load the GIS script, call google.accounts.id.initialize({ client_id, callback }) and renderButton; send the returned ID token (credential) to your backend and verify it:
const ticket = await new OAuth2Client().verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
});
const sub = ticket.getPayload()?.sub; // stable user id Google’s docs state that verifyIdToken checks the signature, aud, exp and iss; use the sub claim, not the email, as the user identifier; for Google Workspace accounts you can additionally check the hd (hosted domain) claim. See Google Workspace.
Checklist (general security practice, not all items taken from the cited pages)
- Keep client ID/secret in environment variables, never in source.
- Match redirect URIs exactly in the Cloud Console; request minimal scopes.
- Validate
state; use PKCE for public clients. - Store refresh tokens encrypted server-side.
Sources
- https://developers.google.com/identity/protocols/oauth2/web-server (fetched 2026-10-05)
- https://developers.google.com/identity/gsi/web/guides/migration (fetched 2026-10-05)
- https://developers.google.com/identity/gsi/web/guides/verify-google-id-token (fetched 2026-10-05)
- https://developers.google.com/identity/gsi/web/guides/overview (fetched 2026-10-05)
Open items
- Code samples were adapted from the docs but not executed in this pass.
- Exact sunset date of
gapi.auth2not stated on the pages fetched. - Browser-only GIS token-client example not included.