Skip to content

Instantly share code, notes, and snippets.

@srestraj
Last active December 13, 2023 04:50
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save srestraj/c61d0a025f53ab7f99bd875eace5cc84 to your computer and use it in GitHub Desktop.
Save srestraj/c61d0a025f53ab7f99bd875eace5cc84 to your computer and use it in GitHub Desktop.
Integrate Google Sign-in and One-tap with Nuxt.js

Integrate Google Sign-in (Popup method) with Nuxt.js - Works in Incognito mode as well

Nuxt 3 version here.
Add GSI client in your nuxt.config.js
export default {
  ...
  head: {
    ...
    script: [
      {
        src: 'https://accounts.google.com/gsi/client',
      },
    ],
    ...
  }
  ...
}
In your login page or component, add the Google Button div (this will be populated by the rendered button)
<div id="googleButton"></div>
Inside your mounted hook, initialize the Google Sign in and render the Sign in button
<template>
  <div id="googleButton"></div>
</template>
<script>

export default {
  mounted() {
    // initialize Google Sign in  
    google.accounts.id.initialize({
        client_id: 'YOUR_CLIENT_ID',
        callback: this.handleCredentialResponse, //method to run after user clicks the Google sign in button
        context: 'signin'
      })
    
    // render button
    google.accounts.id.renderButton(
      document.getElementById('googleButton'),
      { 
        type: 'standard',
        size: 'large',
        text: 'signin_with',
        shape: 'rectangular',
        logo_alignment: 'center',
        width: 250
      }
    )
  },
  
  methods: {
    handleCredentialResponse(response) {
    
      // call your backend API here
      
      // the token can be accessed as: response.credential
    }
  }
}
</script>
If you also want the Google One-tap sign in, use the following inside the component or page you want to display one-tap.
<template>
...
</template>
<script>
  export default {
    mounted() {
      google.accounts.id.initialize({
        client_id: 'YOUR_CLIENT_ID',
        callback: this.handleCredentialResponse,
        context: 'signin',
      })
      google.accounts.id.prompt()
    },
    
    methods: {
      handleCredentialResponse(response) {
        // handle API calls same as above
      }
    }
  }
</script>
@Huucong95
Copy link

image

@srestraj
Copy link
Author

srestraj commented Jun 3, 2022

Hi, @Huucong95 Did you whitelist your localhost domains? You'd need to explicitly add both http://localhost and http://localhost:3000 to your authorized domains as mentioned here.

I hope this helps.

@dhairyanatahn
Copy link

getting an error as google is not defined

@srestraj
Copy link
Author

srestraj commented Sep 8, 2022

getting an error as google is not defined

This is because you're trying to access the "google" property before it's injected and accessible. You need to wait for Google SDK to load so the "google" property is injected into the window object.

Can you include a screenshot of your implementation or create a reproduction in StackBlitz or Codepen?

@ap-1
Copy link

ap-1 commented Sep 27, 2022

I'm getting the same error as @dhairyanatahn, although I'm using Nuxt 3 RC 11 along with its Composition API:
image

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
	/* snip */
	head: {
		script: [
			{ src: "https://apis.google.com/js/api.js" },
			{ src: "https://accounts.google.com/gsi/client" },
		],
	},
});
<!-- login.vue -->
<script setup lang="ts">
import { useAuthStore } from '~/stores/auth';

const { signInNew, signOut } = useAuthStore();

onMounted(() => {
	google.accounts.id.initialize({
		client_id: "id.apps.googleusercontent.com",
		callback: signInNew,
	});

	google.accounts.id.renderButton(document.getElementById("signIn")!, {
		type: "standard",
	});
	google.accounts.id.prompt();
});
</script>

<template>
	<div>
		<button id="signIn"></button>
		<button @click="signOut">Sign out</button>
	</div>
</template>

@srestraj
Copy link
Author

srestraj commented Sep 29, 2022

I'm getting the same error as @dhairyanatahn, although I'm using Nuxt 3 RC 11 along with its Composition API: image

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
	/* snip */
	head: {
		script: [
			{ src: "https://apis.google.com/js/api.js" },
			{ src: "https://accounts.google.com/gsi/client" },
		],
	},
});
<!-- login.vue -->
<script setup lang="ts">
import { useAuthStore } from '~/stores/auth';

const { signInNew, signOut } = useAuthStore();

onMounted(() => {
	google.accounts.id.initialize({
		client_id: "id.apps.googleusercontent.com",
		callback: signInNew,
	});

	google.accounts.id.renderButton(document.getElementById("signIn")!, {
		type: "standard",
	});
	google.accounts.id.prompt();
});
</script>

<template>
	<div>
		<button id="signIn"></button>
		<button @click="signOut">Sign out</button>
	</div>
</template>

Hi @ap-1

for Nuxt 3, you can follow this:

Since Nuxt 3 has changed things a bit, to include the GSI tag in the <head></head> tag, you now have to either use the pre-provided <Head></Head> tag - documentation, or useHead - documentation or, for nuxt.config.ts, you can use it inside app https://v3.nuxtjs.org/api/configuration/nuxt.config#app. For now, I've used the app method.

Include the GSI script tag inside nuxt.config.ts.

export default defineNuxtConfig({
...
  app: {
    head: {
      script: [
        {
          src: 'https://accounts.google.com/gsi/client'
        }
      ]
    }
  },
...
})

Then, in your component or page:

onMounted(() => {
  initializeGoogleSignIn()
})
function initializeGoogleSignIn() {
  if (config.public.GOOGLE_CLIENT_ID != null || '') {
    google.accounts.id.initialize({
      client_id: config.GOOGLE_CLIENT_ID,
      callback: handleCredentialResponse, //method to run after user clicks the Google sign in button
      context: 'signin'
    })
    google.accounts.id.prompt()
  }
}

function handleCredentialResponse(response) {
  console.log(response)
}

I hope this helps.

@timba64
Copy link

timba64 commented Dec 1, 2022

It would be interesting if you show how to make custom button, without google renderButton() :-)

@srestraj
Copy link
Author

@timba64 Unfortunately, I've also not figured this one out yet, LOL

@thomasrobertm
Copy link

hello! I am having the following problem, It says that google is not defined, and I followed all of your steps mentioned above. any help :)?
image

@srestraj
Copy link
Author

srestraj commented Jan 16, 2023

Hi @thomasrobertm , are you using Vue 2 or Vue 3? For Vue 2, you might want to add window. in front of google. This might solve your issue. You can also check if you have added the GSI client in your nuxt.config.js.

@Armogo
Copy link

Armogo commented Feb 2, 2023

I use it in Nuxt 2 project and it worked, thanks a lot!

@Daewon25
Copy link

Hey, I successfully logged in with your code snippet. What I should to do with response in callback? I need to get user info, how to do this? @srestraj

@srestraj
Copy link
Author

Hi @Daewon25 , once you get back the response from the callback, you'd need to pass it to backend for handling. However, if you want to extract the user details from the token you receive in the frontend itself and pass them to the backend, you can do that using jwt-decode.

For example:

<script>
import jwt_decode from 'jwt-decode'

export default {
    ...
  
  methods: {
    handleCredentialResponse(response) {      
      // the token can be accessed as: response.credential
      const token = jwt_decode(response.credential)
      console.log(`ID: ${token.sub}`)
      console.log(`Full Name: ${token.name}`)
      console.log(`Given Name: ${token.given_name}`)
      console.log(`Family Name: ${token.family_name}`)
      console.log(`Image URL: ${token.picture}`)
      console.log(`Email: ${token.email}`)
    }
  }
}
</script>

@Daewon25
Copy link

@srestraj thanks a lot!

@Fanreza
Copy link

Fanreza commented Jul 4, 2023

anyone have example without the google button?

@srestraj
Copy link
Author

srestraj commented Jul 7, 2023

@Fanreza, if you set the ux_mode to redirect and then handle the response server-side, you can create your own custom button. More about this here: https://developers.google.com/identity/gsi/web/reference/js-reference#ux_mode. You can also have a look at this for other options: https://developers.google.com/identity/gsi/web/reference/html-reference. If you want to use it with the pop_up mode itself, I think you can try hiding the default Google button with visibility: hidden or opacity: 0 and then add your custom button. Something like we do for custom file inputs. I haven't tried it myself, but it might work I think.

@Fanreza
Copy link

Fanreza commented Jul 10, 2023

@Fanreza, if you set the ux_mode to redirect and then handle the response server-side, you can create your own custom button. More about this here: https://developers.google.com/identity/gsi/web/reference/js-reference#ux_mode. You can also have a look at this for other options: https://developers.google.com/identity/gsi/web/reference/html-reference. If you want to use it with the pop_up mode itself, I think you can try hiding the default Google button with visibility: hidden and then add your custom button. Something like we do for custom file inputs. I haven't tried it myself, but it might work I think.

i think it will work, google button basically just a button right, we can programatically click it using javascript

@HalaSalim77
Copy link

hi, the login by google button is disapper suddenly and i got this error :

client:128 Uncaught Error
at _.$e (client:128:335)
at new sp (client:227:3)
at bq (client:246:34)
at Oo (client:244:246)
at Object.Po [as renderButton] (client:216:62)
at :8:24
at Object.insertBefore (93a0d79.js:2:45811)
at _ (93a0d79.js:2:59423)
at 93a0d79.js:2:58702
at m (93a0d79.js:2:58934)

what is the problem and how  i can fix it ?

@jirayuboss
Copy link

hi, the login by google button is disapper suddenly and i got this error :

client:128 Uncaught Error at _.$e (client:128:335) at new sp (client:227:3) at bq (client:246:34) at Oo (client:244:246) at Object.Po [as renderButton] (client:216:62) at :8:24 at Object.insertBefore (93a0d79.js:2:45811) at _ (93a0d79.js:2:59423) at 93a0d79.js:2:58702 at m (93a0d79.js:2:58934)

what is the problem and how  i can fix it ?

I also have the same issue. Did you find a way to fix it? Please help.

@srestraj
Copy link
Author

srestraj commented Aug 2, 2023

client:128 Uncaught Error
at _.$e (client:128:335)
at new sp (client:227:3)
at bq (client:246:34)
at Oo (client:244:246)
at Object.Po [as renderButton] (client:216:62)
at :8:24
at Object.insertBefore (93a0d79.js:2:45811)
at _ (93a0d79.js:2:59423)
at 93a0d79.js:2:58702
at m (93a0d79.js:2:58934)

Hi @HalaSalim77, could you send us the code snippet you have set up? Thanks.

@srestraj
Copy link
Author

srestraj commented Aug 4, 2023

Hi @HalaSalim77 and @jirayuboss , it looks like the issue is happening from the Google's side itself. As mentioned here: google/google-api-javascript-client#873 (comment), if you remove the width property or add px to it, it's fixed again. example:

google.accounts.id.renderButton(
      document.getElementById("gBtn"), {
        type: 'standard',
        size: 'large',
        text: 'signup_with',
        shape: 'pill',
        logo_alignment: 'left',
        width: '250px'
      }
)

I hope this helps.

@DwbpleaseCallMe
Copy link

Hello. I just want to display Google one tap on the website.

When I used your method to fix my nuxt2 project, there was no button on my website, but it was successful on other vue2 projects

//nuxt.config.js
{
src: 'https://accounts.google.com/gsi/client',
async: true,
defer: true,
},

//index.vue
mounted() {

setTimeout(() => {
  if (window.google && !this.token) {
    window.google.accounts.id.initialize({
      client_id: 'xxx',
      callback: this.handleGoogleOneTapCallback,
      context: 'signin',
    });
    window.google.accounts.id.prompt();


  }

}, 3000);

},

@srestraj
Copy link
Author

Hello. I just want to display Google one tap on the website.

When I used your method to fix my nuxt2 project, there was no button on my website, but it was successful on other vue2 projects

//nuxt.config.js { src: 'https://accounts.google.com/gsi/client', async: true, defer: true, },

//index.vue mounted() {

setTimeout(() => {
  if (window.google && !this.token) {
    window.google.accounts.id.initialize({
      client_id: 'xxx',
      callback: this.handleGoogleOneTapCallback,
      context: 'signin',
    });
    window.google.accounts.id.prompt();


  }

}, 3000);

},

@DwbpleaseCallMe, what's the error that you're seeing?

@DwbpleaseCallMe
Copy link

Hello. I just want to display Google one tap on the website.
When I used your method to fix my nuxt2 project, there was no button on my website, but it was successful on other vue2 projects
//nuxt.config.js { src: 'https://accounts.google.com/gsi/client', async: true, defer: true, },
//index.vue mounted() {

setTimeout(() => {
  if (window.google && !this.token) {
    window.google.accounts.id.initialize({
      client_id: 'xxx',
      callback: this.handleGoogleOneTapCallback,
      context: 'signin',
    });
    window.google.accounts.id.prompt();


  }

}, 3000);

},

@DwbpleaseCallMe, what's the error that you're seeing?

There was no error, it's just that there are no buttons displayed on the page

@srestraj
Copy link
Author

Hi @DwbpleaseCallMe, to display the button, you'd need to add the following as well.

setTimeout(() => {
  if (window.google && !this.token) {
    window.google.accounts.id.initialize({
      client_id: 'xxx',
      callback: this.handleGoogleOneTapCallback,
      context: 'signin',
    });

    // for button rendering
    window.google.accounts.id.renderButton(
      document.getElementById('googleButton'), // here, replace "googleButton" with the ID of your element that you want to populate with GSI.
      { 
        type: 'standard',
        size: 'large',
        text: 'signin_with',
        shape: 'rectangular',
        logo_alignment: 'center',
        width: '250px'
      }
    );
    window.google.accounts.id.prompt();


  }

}, 3000);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment