Code

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's aud) must use the WEB client ID. The Android client only registers your signature; it never appears in the token's aud. If the app passes the Android ID, the server sees a mismatched aud during verification and throws:

invalid google token: idtoken: audience provided does not match aud claim in the JWT

The 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.