This will cover how you can setup "Continue with Google" login option on your website/ web app.
Link to this project's repo can be found here
Setting Up NUXT3 Project
npx nuxi@latest init <project-name>
Installing And Set-up of Required Dependency
Installing Supabase Nuxt Module
@nuxtjs/supabase is a Nuxt module for integrating Nuxt web app with Supabase.npx nuxi@latest module add supabase
Adding @nuxtjs/supabase
to modules
section of nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['@nuxtjs/supabase'],
})
Setting Up .env
SUPABASE_URL="https://example.supabase.co" SUPABASE_KEY="<your_key>"
Supabase Project Setup
Create a new project on Supabase.
Copy the
anon
public
key toSUPABASE_URL
andservice_role
secret
key toSUPABASE_KEY
in your.env
folder.Go to
Authentication
from you project dashboard andproviders > Google > Enable Sign in with Google
. Copy theCallback URL (for OAuth)
for use in further reference. The other fileds likeClient ID (for OAuth)
andClient Secret (for OAuth)
will be filled once the project is set up on Google Developer Console.Go to console.developers.google.com and set-up a new project.
Go to Dashboard of your project >
Go to API's overview
>OAuth consent screen
and set user type toExternal
, after that fill up the required app information likeName
,email
etc. Save and contiue the other steps if no special information has to be provided.After the above steps, from the project dashboard itself go to
Credentials > Create New > OAuth client ID > Application type -> Web Application
. InAuthorized redirected URLs
paste the URL we copied while enabling Google OAuth in Supabase and hitCreate
. Copy theClient ID
andClient secret
.Paste the
Client ID
andClient secret
toClient ID (for OAuth)
andClient Secret (for OAuth)
in Supabase Auth set-up respectively and hitSave
.
Project Structure
Create pages
folder and add index.vue
, login.vue
, confirm.vue
and secure.vue
.
index.vue
: Page that is accessible even with or without authentication.login.vue
: Page on which authentication will be performed. -confirm.vue
: Page that confirms authentication of user and redirects to appropriate page (here/secure
).secure.vue
: Page that cannot be accessed without authentication.
Create middleware
folder and add auth.js
to it.
auth.js
: Middleware that'll keep check that the user isn't able to access the login page after loggin in / signing in.
Final folder structure should look something similar to this :
├── .nuxt
├── middleware
│ ├──auth.js
├── pages
│ ├── index.vue
│ ├── login.vue
│ ├── confirm.vue
│ ├── secure.vue
├── public
├── server
├── .env
├── .gitignore
├── app.vue
├── nuxt.config.ts
└── README.md
Coding
nuxt.config.ts
Tinkering Supabase in nuxt.config.ts
as by default @nuxtjs/supabase module secures all routes, while in this case we want to ignore the home ('/') route so we will modify the settings.
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
modules: ['@nuxtjs/supabase'],
supabase: {
redirectOptions: {
login: '/login',
callback: '/confirm',
include: undefined,
exclude: ['/'],
cookieRedirect: false,
},
},
})
index.vue
Index page won't require any authentication to access and in this case for extras, we have added a simple login button and some text.
<script setup>
const login_to = ()=> {
return navigateTo('/login')
}
</script>
<template>
<div>
<div class="for-button">
<button type="button" @click="login_to">Login</button>
</div>
<div>
<h2 class="rotate">Index</h2>
<h2>Page</h2>
</div>
</div>
</template>
<style lang="css" scoped>
div {
font-family: "didot";
}
h2 {
font-size: 100px;
margin: 0;
writing-mode: vertical-lr;
text-align: center;
line-height: .9;
}
.rotate {
transform: rotate(180deg);
}
div {
display: grid;
height: 100vh;
justify-content: center;
align-content: center;
grid-template-columns: max-content max-content;
}
body {
margin: 0;
}
.for-button {
position: relative;
flex: 1;
}
button {
position: fixed;
cursor: pointer;
top: 15px;
right: 15px;
border: none;
border-radius: 12px;
background-color: rgba(104, 85, 224, 1);
color: white;
padding: 13px 30px;
font-size: 18px;
transition-duration: 0.4s;
}
button:hover {
border: 1px solid rgba(104, 85, 224, 1);
background-color: white;
color: rgba(104, 85, 224, 1);
}
</style>
login.vue
Login page contains the Continue with Google
button along with the logic to login in and redirect the user to /confirm
route which will confim if the user is signed-in or not.
<script setup lang="ts">
const supabase = useSupabaseClient()
const signInWithOAuth = async () => {
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'http://localhost:3000/confirm',
},
})
if (error) console.log(error)
}
const signOut = async () => {
const { error } = await supabase.auth.signOut()
if (error) console.log(error)
}
definePageMeta({
middleware: ["auth"],
})
</script>
<template>
<div>
<button type="button" class="login-with-google-btn" @click="signInWithOAuth" >
Sign in with Google
</button>
</div>
</template>
<style scoped>
.login-with-google-btn {
transition: background-color .3s, box-shadow .3s;
padding: 12px 16px 12px 42px;
border: none;
border-radius: 3px;
box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 1px 1px rgba(0, 0, 0, .25);
color: #757575;
font-size: 14px;
font-weight: 500;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMTcuNiA5LjJsLS4xLTEuOEg5djMuNGg0LjhDMTMuNiAxMiAxMyAxMyAxMiAxMy42djIuMmgzYTguOCA4LjggMCAwIDAgMi42LTYuNnoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik05IDE4YzIuNCAwIDQuNS0uOCA2LTIuMmwtMy0yLjJhNS40IDUuNCAwIDAgMS04LTIuOUgxVjEzYTkgOSAwIDAgMCA4IDV6IiBmaWxsPSIjMzRBODUzIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNCAxMC43YTUuNCA1LjQgMCAwIDEgMC0zLjRWNUgxYTkgOSAwIDAgMCAwIDhsMy0yLjN6IiBmaWxsPSIjRkJCQzA1IiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNOSAzLjZjMS4zIDAgMi41LjQgMy40IDEuM0wxNSAyLjNBOSA5IDAgMCAwIDEgNWwzIDIuNGE1LjQgNS40IDAgMCAxIDUtMy43eiIgZmlsbD0iI0VBNDMzNSIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTAgMGgxOHYxOEgweiIvPjwvZz48L3N2Zz4=);
background-color: white;
background-repeat: no-repeat;
background-position: 12px 11px;
&:hover {
box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 2px 4px rgba(0, 0, 0, .25);
}
&:active {
background-color: #eeeeee;
}
&:focus {
outline: none;
box-shadow:
0 -1px 0 rgba(0, 0, 0, .04),
0 2px 4px rgba(0, 0, 0, .25),
0 0 0 3px #c8dafc;
}
&:disabled {
filter: grayscale(100%);
background-color: #ebebeb;
box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 1px 1px rgba(0, 0, 0, .25);
cursor: not-allowed;
}
}
div {
text-align: center;
padding-top: 2rem;
}
</style>
confirm.vue
Confirm page is just confirms if user is signed-in or not and then redirects them to appropriate page (here /secure
).
<script setup lang="ts">
const user = useSupabaseUser()
// Get redirect path from cookies
const cookieName = useRuntimeConfig().public.supabase.cookieName
const redirectPath = useCookie(`${cookieName}-redirect-path`).value
watch(user, () => {
if (user.value) {
// Clear cookie
useCookie(`${cookieName}-redirect-path`).value = null
// Redirect to path
return navigateTo(redirectPath || '/secure');
}
}, { immediate: true })
</script>
<template>
<div>Waiting for login...</div>
</template>
secure.vue
Secure page cannot be accessed without authentication and has logic to log-out a user.
<script setup>
const supabase = useSupabaseClient()
const signOut = async () => {
const { error } = await supabase.auth.signOut()
if (error) console.log(error)
navigateTo('/login')
}
</script>
<template>
<div class="centre">
<div class="space">secure page</div>
<button type="button" @click="signOut">Log Out</button>
</div>
</template>
<style scoped>
.centre {
text-align: center;
padding-top: 2rem;
font-size: 30px;
}
.space {
padding-top: 50px;
padding-bottom: 50px;
}
button {
border: none;
font-size: 25px;
background-color: rgb(39, 39, 39);
color: white;
padding: 15px 32px;
border-radius: 12px;
transition-duration: 0.4s;
}
button:hover {
background-color: white;
color: black;
border: 1px solid black;
}
</style>
Running Project
npm run dev
Or as per your package manager.
Summary and links
The following tutorial covered how to add OAuth functionality (Google OAuth to be specific) to your website/ webapp.
You can find more info from the Official documentations :