Create google.go — the server verifies the Google token
We never trust the Google token blindly — the server verifies it. In the code column — the backend's auth/google.go. idtoken.Validate does all the hard work: it checks Google's signature, aud (must equal your Web client ID), exp and iss. If anything is off — err, and we don't trust the token.
The HTTP part (handlers.go) — "find or create" the user and issue our JWT:
func (h *Handler) GoogleAuth(w http.ResponseWriter, r *http.Request) {
var body struct{ IDToken string `json:"id_token"` }
json.NewDecoder(r.Body).Decode(&body)
googleUser, err := VerifyGoogleToken(body.IDToken) // ← Google verification
if err != nil {
http.Error(w, "invalid google token", http.StatusUnauthorized) // 401
return
}
// Find the user by email; if none — create one (a Google user has no password)
user, err := h.queries.GetUserByEmail(r.Context(), googleUser.Email)
if errors.Is(err, sql.ErrNoRows) {
user, _ = h.queries.CreateUser(r.Context(), sqlc.CreateUserParams{
Email: googleUser.Email, PasswordHash: nil, // nil = Google-only
})
}
token, _ := CreateToken(int(user.ID), user.Email) // ← OUR session JWT (like 8a)
writeJSON(w, http.StatusOK, authResponse{Token: token})
}
The result is { "token": "..." }, identical to the /login response. The server reads GOOGLE_WEB_CLIENT_ID from the environment (.env locally, Railway in production).
Gotcha (Web vs Android client — the most common!). Google Cloud Console gives you two client IDs. Intuition says "use the Android ID, it's an Android app" — wrong. Both the app (
setServerClientId) and the server (idtoken.Validate'saud) must use the WEB client ID. The Android client only registers your signature; it never appears in the token'saud. If the app passes the Android ID, the server sees a mismatchedaudduring verification and throws:invalid google token: idtoken: audience provided does not match aud claim in the JWTThe rule: both sides use the Web client ID. The Android client must exist (with the correct SHA-1), but you never use its ID in code.