Google's sign-in offering has been reworked repeatedly over the years, and the technical route you take today depends on how much control you want over the UI. The recommended path leans on a browser-side client library; a second, more generic path builds on OpenID Connect.

The Google Identity Service client library is the currently favored implementation, and it produces a familiar button:

Sample screen of sign-in with Google

Clicking it opens a popup with Google's own account chooser:

The popup window that opens for Google sign-in

A "one tap" variant shows the signed-in Google user in an in-page popup instead, which also behaves well on mobile.

Setup starts at https://console.cloud.google.com/apis/credentials, where you register an application and receive a client ID and secret. The secret plays no part in this route but is required for the OpenID Connect one.

The button itself, plus all the client-side logic, comes from a Google JS library called GSI, embedded in the page with configuration attributes — there is substantial room to customize them according to the HTML reference docs. Server-side, the page is served from the root "/" handler, and a callback URL is registered so the Google auth server can redirect there after confirming the user's identity. That redirect carries the user's details as a JSON Web Token (JWT), which our application must validate before accepting the sign-in.

Verification uses the google.golang.org/api/idtoken package from the official GCP client library for Go:

func callbackHandler(w http.ResponseWriter, req *http.Request) {
  defer req.Body.Close()

  if err := req.ParseForm(); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
  }

  // The following steps follow
  // https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
  //
  // 1. Verify the CSRF token, which uses the double-submit-cookie pattern and
  //    is added both as a cookie value and post body.
  token, err := req.Cookie("g_csrf_token")
  if err != nil {
    http.Error(w, "token not found", http.StatusBadRequest)
    return
  }

  bodyToken := req.FormValue("g_csrf_token")
  if token.Value != bodyToken {
    http.Error(w, "token mismatch", http.StatusBadRequest)
  }

  // 2. Verify the ID token, which is returned in the `credential` field.
  //    We use the idtoken package for this. `audience` is our client ID.
  ctx := context.Background()
  validator, err := idtoken.NewValidator(ctx)
  if err != nil {
    panic(err)
  }
  credential := req.FormValue("credential")
  payload, err := validator.Validate(ctx, credential, GoogleClientID)
  if err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
  }

  // 3. Once the token's validity is confirmed, we can use the user identifying
  //    information in the Google ID token.
  for k, v := range payload.Claims {
    fmt.Printf("%v: %v\n", k, v)
  }
}

Comments in the sample walk through the documented steps in order. What comes out at the end is user information from the token — the Google email and the registered name, which establish both identity and the existence of a real Google account.

OpenID Connect as a more flexible alternative

OpenID Connect is an authentication protocol layered on OAuth 2, documented separately by Google. It accepts no particular JS or HTML elements, so it can be folded into any flow, much like the GitHub approach from a previous post. Two sample implementations follow the official docs:

  1. go-oidc, full code here.
  2. gologin, full code here. The gologin package also backs the earlier GitHub writeup; the delta between the two is tiny.

[1]Please refer to my description of OAuth to understand the redirection flow. This Google sign-in flow still relies on OAuth underneath.

Comments are welcome by email.