danny@intricatecloud:~/blog$ cat << EOF

Adding Google Sign-in to your webapp in 2025

| 7 min read

Gotten stuck in a rabbit hole figuring out how to add "Log In with Google" to your web app?

This was an earlier series on my site from way back, but it looks like the landscape has changed so much - its worth trying to address how to do simple things like sign-in with google and use google oauth.

In this series now updated for 2025, I'll cover:

  • why you might want to use the Sign-In for Website JS library, and getting started with it
  • Adding the library via javascript
  • adding it to an existing project (angular & react)

Their docs give you a decent place to start if you know what you're looking for, but there's a few areas where their docs and other guides online can be confusing. There's also minimal guidance as to how to use it within existing projects.

"How do I add Log in with Google?" is a very loaded question, but we can distill it down to these 3 use cases

  • Do you just want to be able to see the users name and picture, maybe their email from an HTML page? Use the Sign-In with Google for Web (full walkthrough below).
  • If you are using a third-party authentication platform such as Firebase, Auth0, OneIdentity, use the OpenID Connect protocol
  • Do you have your own database of users, and you want to add Google as a login option? The Sign-In with Google for Web may be the right option.
  • Do you want to be able to interact with the users Google account and do things like see their calendar, or create a Google doc? You'll need to use their OAuth protocol.

For the purposes of this post, I'm going to explore the Sign-In with Google for Web GSI library which uses the OpenID Connect protocol.The goal of this authentication library is to let you identify a user with their Google account on a web page. Thats it.

So when should I Sign-In with Google for Web?

Good reasons to use it:

  • you have some content stored on a backend service that you deliver via an API only after the user has signed in
  • you only have to support Google/G-Suite users
  • you dont need to authorize users (e.g. allow some users to be admins)
  • your site is public
  • you have a single-page/static app

When not to use it:

  • you want to access a Google service on behalf of a user such as reading a doc/spreadsheet, search Drive, or send a Gmail.
  • you are using an existing authentication system with Google support such as Firebase, Keycloak, Auth0

Use cases where it's worth considering using the OAuth 2.0 Flow instead:

  • you have pre-existing user content stored on a backend service
  • you have an internal site that you want to restrict to users from a specific domain. (e.g. only users from @example.com should see it)
  • you want to prevent people from seeing your page unless they've logged in. The best you can do with this library is show/hide elements on the page if the user logged in, but thats enough if you only load data from an API after a user has logged in but not for gating static content.

These use cases require server-side handling alongside a working Sign-In with Google implementation. (Coming soon!)

The library is designed to be used with HTML/JS and only interacts with your page via the "Sign in with Google" button. You can integrate this with other frameworks like Angular/React which I'll be covering in the next part of this series.

Adding Google Sign-in step-by-step

1. Get your Google Sign-In Client Credentials

Before adding in the button, you first need to create a client ID/secret which is a way of identifying that you, the developer, are allowing users to get the identity information from Google and delivered to your website.

  • Go to the Google API Console - https://console.developers.google.com/apis/dashboard
  • Create a new project, or use an existing project if you already have one set up.
  • Then click on Credentials -> Create Credentials -> OAuth Client ID

Create OAuth Client ID

Here's what I put in for this demo:

Name: google-auth-demo Authorized Javascript Origins:

  • http://localhost:8000
  • http://localhost

Authorized Redirect URIs: You can ignore for the purpose of this post.

[!NOTE] If you have a plain HTML file that you load in your browser using a file path like /home/dperez/index.html, this won't work. You'll need to "serve" your website so that you have a URL, even if its just localhost. You can use python -m http.server 8080 to serve up your current directory or you can use an npm package like http-server.

Add Authorized JavaScript Origins

[!IMPORTANT] You must explicitly add http://localhost to your Authorized Javascript Origins. Adding a different port like http://localhost:8080 will not automatically authorize http://localhost.

You'll then get a Client ID & Client Secret. These are 2 identifiers for your Oauth Client. At this point, you have authorized that Client ID to accept logins and return you the users information. You'll only need the Client ID for the rest of this walkthrough.

Get Client ID and Secret

Theres a warning that says ss of June 2025, you can no longer view your client secret after you leave this page. For the purpose of this walkthrough, we don't need it. If you wind up needing it later, you can click on Add Secret to create a new one. However, I'm still able to get to it in the screenshot.

At this point, you have a Client ID available and you can proceed!

2. Add the Google Sign-In Button

You can start with an empty index.html file with the contents below

<!DOCTYPE html>
<html>
<head>
  <title>Google Auth Demo</title>
</head>
<body>
  <h1>Welcome to the Demo</h1>
</body>
</html>

Add the library + credentials + button to your HTML

In your index.html page, add the following to your HTML page:

...
<head>
  ...
  <script src="https://accounts.google.com/gsi/client" async defer></script>
</head>
<body>
  <h1>Welcome to the Demo</h1>
  <div id="g_id_onload"
      data-client_id="YOUR_CLIENT_ID.apps.googleusercontent.com"
      data-auto_prompt="false">
  </div>
  <div class="g_id_signin"
        data-type="standard"
        data-size="large"
        data-theme="outline"
        data-text="sign_in_with"
        data-shape="rectangular"
        data-logo_alignment="left">
</body>

The script tag grabs the GSI library from Google using async defer which means that the rest of your application can load while this library is downloaded to your browser and will not prevent the page from loading.

Once that library is loaded, it will automatically search for 2 elements with ID g_id_onload and g_id_signin.

The g_id_onload div element is where you will add the client id that you previously generated in the console. There are other options that you can set, more described here

The g_id_signin div element lets you define additional attributes to control the look and feel of the button. It also lets you "connect" your button to any of your Javascript code. A listing of all the available attributes to define are described here

At this point, if you refresh your page, you should see a pretty Sign-In button. If you click it, you'll go through your Google Login flow in a popup. Then it goes back to the page and the button didn't change state yet. Hmm.

Let's try adding in the callback to see if this is succeeding

<div id="g_id_onload"
      data-client_id="YOUR_CLIENT_ID.apps.googleusercontent.com"
      data-callback="handleCredentialResponse"
      data-auto_prompt="false">
...
<script>
  function handleCredentialResponse(response) {
    console.log("Encoded JWT ID token: " + response.credential);
  }
</script>

Booya - that works. I am able to see the token in the console but I do not yet see the button change appearance.

When I open up the console to see errors, I see an error message [GSI_LOGGER]: The given origin is not allowed for the given client ID.

The site is being served from http://localhost:8000, and I've confirmed that setting in the console. Bummer.

Ok, lets try the Javascript implementation now.

<body>
  <h1>Welcome to the Demo</h1>
  <div id="gsi_button"></div>
  <script>
    function handleCredentialResponse(response) {
        console.log("Encoded JWT ID token: " + response.credential);
      }
      window.onload = function () {
        google.accounts.id.initialize({
          client_id: "YOUR_CLIENT_ID.apps.googleusercontent.com",
          callback: handleCredentialResponse
        });
        google.accounts.id.renderButton(
          document.getElementById("gsi_button"),
          { theme: "outline", size: "large" }  // customization attributes
        );
        google.accounts.id.prompt(); // also display the One Tap dialog
      }
  </script>
</body>

Loading the page, I get through a successful sign-in and I see the log statement with the token information. However, I still do not see a change in the visual appearance of the button.

I came across this Stack Overflow issue - https://stackoverflow.com/questions/68438293/the-given-origin-is-not-allowed-for-the-given-client-id-gsi which states that in the Google Client ID settings, you need to include both http://localhost and http://localhost:8000. I was using http://127.0.0.1:8000 so the origin didn't match. Once I accessed the site using http://localhost:8000 instead, it saw that I was already signed in. And updated the state of the button. Win!

TODO insert pic

Great, we're most of the way there! But in its current form, this is still useless.

3. Identify the user

In the 2 examples above, we defined a callback called handleCredentialResponse

function handleCredentialResponse(response) {
  console.log("Encoded JWT ID token: " + response.credential);
}

At this point, you have this credential object which contains all the useful information about the currently signed in user except it is encoded in an ID token.

An ID token is usually a string of some kind usually containing json that is encoded using base64 to make it easy to share around as a string. If we add jwt_decode to the project, we can easily read the info in the token

const decoded = jwt_decode(response.credential);
console.log("Decoded payload:", decoded);

This shows the result

{
    "iss": "https://accounts.google.com",
    "azp": "1006632388683-oe9a58l64grlf5cdecdjjna9rvu7vliv.apps.googleusercontent.com",
    "aud": "1006632388683-oe9a58l64grlf5cdecdjjna9rvu7vliv.apps.googleusercontent.com",
    "sub": "108716981921401766503",
    "email": "user@example.com",
    "email_verified": true,
    "nbf": 1757556093,
    "name": "danny perez",
    "picture": "https://lh3.googleusercontent.com/a/ACg8ocKXJnZr6LWNfdACAQGFvVVNSaWYS4_Ghq7Ap-JM-7O-dzLkhNqQ=s96-c",
    "given_name": "danny",
    "family_name": "perez",
    "iat": 1757556393,
    "exp": 1757559993,
    "jti": "6cb3411828a7c3bd2fa21816ba94a6f385419e1d"
}

Aha! There is the link to my picture that I can display on my page now.

You'll also notice your callback handleCredentialResponse will be called with an argument containing the credential response from Google. If you refresh the page, you'll notice that

  1. You're automatically signed in
  2. The button gets updated very shortly after refresh (1s delay)

4. Sign out

Add a sign out button after the user has signed in by adding a button in your index.html and the click handler in your javascript.

index.html

<body>
...
  <button onclick="signOut()">Sign out</button>
</body>
function signOut() {
  gapi.auth2.getAuthInstance().signOut().then(function() {
    console.log('user signed out')
  })
}

Great! At this point, we've added a Sign-In with Google button and we can identify the user by name/email. Also, thats it. Thats all this library can help you do.

...but what about basic things like saving users data to the backend? or showing an admin page?! Why isn't this in the docs?! That'll be covered on the next post coming 7/22 - using the js library to add it to your site without HTML.

Update: Here's the link for part 2 of the series - Using the js library

References:

  • Entrypoint into the Sign in With Google docs https://developers.google.com/identity/siwg

  • Overview - https://developers.google.com/identity/gsi/web/guides/overview