Skip to content

Verify Users on Your Server

GoogleSignInButton and useOneTap hand your page a credential: an ID token, a JWT signed by Google that says who the user is. Your API can’t take the browser’s word for it, since anyone can send it a made-up token. Before you create an account or a session, your server has to check that Google signed the token, that it was issued for your client ID, and that it hasn’t expired.

decodeCredential reads the token without checking any of that. Use it to show the user’s name and picture, never to decide who they are.

  1. The user signs in with the button or One Tap, and your page gets the credential.

  2. The page posts it to your server.

  3. The server verifies it with google-auth-library and reads the user from it.

  4. The server starts a session, and from then on your app uses the session, not the token. The ID token expires after about an hour.

Claim What it is
sub The user’s Google account ID. Unique and never changes: key your users on it.
email The user’s email address. It can change, so don’t use it as the user’s ID.
email_verified Whether Google has verified the email address.
name, given_name, family_name The user’s name, if they share it.
picture A URL of the user’s profile picture.
hd The user’s Google Workspace domain. Missing for personal accounts.
aud The client ID the token was issued for. verifyIdToken checks it against yours.
exp When the token expires, in seconds since the epoch.
nonce The nonce you passed to the button or One Tap, if any.
src/components/SignIn.vue
<script setup lang="ts">
import {
GoogleSignInButton,
type CredentialSuccessResponse,
} from "vue3-google-signin";
async function onSuccess({ credential }: CredentialSuccessResponse) {
const response = await fetch("/api/auth/google", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ credential }),
});
if (!response.ok) throw new Error("Google sign-in failed");
// The server set a session cookie: load the signed-in part of your app
}
</script>
<template>
<GoogleSignInButton @success="onSuccess" />
</template>

The same handler works as useOneTap’s onSuccess.

verifyIdToken fetches and caches Google’s public keys, checks the signature, the audience, the issuer and the expiry, and throws if any check fails.

Any backend works. This one is Express:

Terminal window
npm install express express-session google-auth-library
server.js
import express from "express";
import session from "express-session";
import { OAuth2Client } from "google-auth-library";
const google = new OAuth2Client();
const app = express();
app.use(express.json());
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
}),
);
app.post("/api/auth/google", async (req, res) => {
let payload;
try {
const ticket = await google.verifyIdToken({
idToken: req.body.credential,
audience: process.env.GOOGLE_CLIENT_ID,
});
payload = ticket.getPayload();
} catch {
return res.status(401).json({ error: "Invalid Google credential" });
}
if (!payload?.email_verified) {
return res.status(401).json({ error: "Email not verified" });
}
// Look the user up by payload.sub in your database, or create them
req.session.user = {
id: payload.sub,
email: payload.email,
name: payload.name,
picture: payload.picture,
};
res.json(req.session.user);
});
app.post("/api/auth/logout", (req, res) => {
req.session.destroy(() => res.sendStatus(204));
});
app.listen(3001);

audience is the same client ID you gave the plugin.

Pass hd to the button or One Tap so Google only offers accounts from your domain:

<GoogleSignInButton hd="example.com" @success="onSuccess" />

That is only a hint to Google’s UI. Enforce it on the server, after verifyIdToken:

if (payload.hd !== "example.com") {
throw createError({
statusCode: 403,
statusMessage: "Use your work account",
});
}

Signing out ends your session; the user stays signed in to Google. Clear the session, then call disableAutoSelect so that One Tap with autoSelect doesn’t sign them straight back in.

async function signOut() {
await fetch("/api/auth/logout", { method: "POST" });
window.google?.accounts.id.disableAutoSelect();
}

To remove your app from the user’s Google account as well, for example when they delete their account with you, call idRevoke with their sub or email address. Google then shows them the consent screen again the next time they sign in.