How to Add Supabase Authentication to a Vue App

Adding user authentication is a fundamental part of many web applications. Supabase is an open source Firebase alternative that provides an easy-to-use authentication system. In this tutorial, we‘ll walk through how to add Supabase auth to a Vue 3 app, including email/password and magic link login.

We‘ll cover:

  • Setting up a Supabase project
  • Installing and initializing the Supabase JS client in a Vue app
  • Building Vue components for sign up, sign in, and sign out
  • Using the Supabase auth session to conditionally render components
  • Enabling magic link authentication

By the end, you‘ll have a fully working auth system in your Vue app. Let‘s dive in!

Prerequisites

This tutorial assumes you have some familiarity with Vue 3, especially the Composition API. It uses Vite as the build tool, but the concepts apply to any Vue setup.

You‘ll need:

  • Node.js version 14.18+
  • A Supabase account (if you don‘t have one, sign up for free)

Set up a Supabase project

First, let‘s create a new Supabase project. In the Supabase dashboard, click "New Project". Enter a name for your project and select the region closest to you. Wait for the new database to launch.

New Supabase project

Once the project is ready, go to the "Authentication" section in the sidebar and click "Settings". Make sure "Enable email confirmations" is unchecked to keep things simple for this demo.

Disable email confirmations

Create a new Vue project

Open up a terminal and run the following command to create a new Vue project:

npm init vue@latest

You‘ll be prompted to select some options. Choose Vue 3 and give the project a name like "supabase-auth-demo". After the project is set up, cd into the project directory.

Install the Supabase client

To interact with Supabase in our Vue app, we need to install the Supabase JavaScript library. Run this command in your project:

npm install @supabase/supabase-js

Initialize Supabase in the Vue app

To initialize the Supabase client in the app, we need the project‘s API URL and anon key. You can find these in the Supabase dashboard under "Project Settings > API".

It‘s a good practice to store these values in environment variables instead of hardcoding them. Create a file called .env at the root of your project with the following contents:

VITE_SUPABASE_URL=YOUR_SUPABASE_PROJECT_API_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_PROJECT_ANON_KEY 

Make sure to replace YOUR_SUPABASE_PROJECT_API_URL and YOUR_SUPABASE_PROJECT_ANON_KEY with your actual values.

Now create a new file src/supabase.js and add the following code:

import { createClient } from ‘@supabase/supabase-js‘

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseKey)

This initializes a Supabase client instance that we can import and use anywhere in our Vue app.

Create the Auth components

Let‘s create some Vue components to handle sign up, sign in, and sign out.

First, create a new component file src/components/SignUp.vue:

<template>
  <form @submit.prevent="signUp">
    <h2>Sign Up</h2>
    <div>
      <label for="email">Email</label>
      <input type="email" id="email" v-model="email" required />
    </div>
    <div>
      <label for="password">Password</label>  
      <input type="password" id="password" v-model="password" required />
    </div>
    <button type="submit">Sign Up</button>
  </form>
</template>

<script setup>
import { ref } from ‘vue‘
import { supabase } from ‘../supabase‘

const email = ref(‘‘)
const password = ref(‘‘) 

async function signUp() {
  try {
    const { user, error } = await supabase.auth.signUp({
      email: email.value,
      password: password.value,
    })
    if (error) throw error
    // todo after sign up
  } catch (error) {
    alert(error.message)
  }
}
</script>

This renders a sign up form with email and password fields. When the form is submitted, it calls the signUp function which uses the supabase.auth.signUp method to create a new user.

Similarly, create a SignIn.vue component:

<template>
  <form @submit.prevent="signIn">
    <h2>Sign In</h2>
    <div>
      <label for="email">Email</label>
      <input type="email" id="email" v-model="email" required />  
    </div>
    <div>
      <label for="password">Password</label>
      <input type="password" id="password" v-model="password" required />
    </div>
    <button type="submit">Sign In</button>
  </form>
</template>

<script setup>
import { ref } from ‘vue‘  
import { supabase } from ‘../supabase‘

const email = ref(‘‘)
const password = ref(‘‘)

async function signIn() {
  try {
    const { user, error } = await supabase.auth.signIn({
      email: email.value,
      password: password.value,
    })
    if (error) throw error
    // todo after sign in
  } catch (error) {
    alert(error.message)
  }  
} 
</script>

The sign in form works similarly to sign up, except it calls supabase.auth.signIn to authenticate an existing user.

Finally, add a component to display the user‘s info and sign out:

<!-- src/components/Account.vue -->
<template>
  <div>
    <h2>Welcome, {{ user.email }}</h2>
    <button @click="signOut">Sign Out</button>
  </div>  
</template>

<script setup>
import { supabase } from ‘../supabase‘ 

const user = supabase.auth.user()

async function signOut() {
  try {
    const { error } = await supabase.auth.signOut()
    if (error) throw error
    // todo after sign out
  } catch (error) {
    alert(error.message)
  }
}
</script>

This gets the currently logged in user and displays their email. The signOut method ends the user‘s session.

Add auth state handling

To keep track of the user‘s auth state across the app, we can store it in reactive state. Create a new store file:

// src/store.js 
import { reactive } from ‘vue‘
import { supabase } from ‘./supabase‘

export const store = {
  state: reactive({
    user: {},
  }),

  async init() {
    const user = supabase.auth.user()
    this.state.user = user

    supabase.auth.onAuthStateChange((event, session) => {
      console.log(event, session)
      this.state.user = session?.user  
    })
  }
}

This creates a reactive state object with a user property. The init method gets the initial user and subscribes to Supabase‘s onAuthStateChange to update the user whenever the auth state changes.

Call store.init() in your main app file:

<!-- src/App.vue -->  
<script setup>
import { store } from ‘./store‘

store.init()
</script>

With this in place, we can access the user info from any component using store.state.user. Let‘s update the SignUp and SignIn components to redirect after a successful auth request:

// SignUp.vue
async function signUp() {
  // ...
  // Redirect after sign up
  window.location.href = ‘/dashboard‘
}

// SignIn.vue  
async function signIn() {
  // ...  
  // Redirect after sign in
  window.location.href = ‘/dashboard‘
}

Add conditional rendering based on auth state

Now we can conditionally show and hide components based on the user‘s auth state. Update your App.vue file:

<template>
  <div>
    <nav>
      <RouterLink to="/" v-if="!user">Home</RouterLink>
      <RouterLink to="/login" v-if="!user">Sign In</RouterLink>
      <RouterLink to="/register" v-if="!user">Sign Up</RouterLink> 
      <RouterLink to="/dashboard" v-if="user">Dashboard</RouterLink>
      <RouterLink to="/account" v-if="user">Account</RouterLink>
    </nav>

    <RouterView />
  </div>
</template>

<script setup>
import { store } from ‘./store‘ 

const user = computed(() => store.state.user)
</script>

This shows the home, sign in and sign up links when there‘s no authenticated user, and the dashboard and account links when a user is signed in.

For the router views:

<!-- src/views/Dashboard.vue --> 
<template>
  <div>

    <p>Only authenticated users can see this.</p>
  </div>
</template>

<script setup>
import { store } from ‘../store‘

if (!store.state.user) {
  // Redirect to login if not signed in
  window.location.href = ‘/login‘
}
</script>

<!-- src/views/Account.vue -->
<template> 
  <div>
    <Account />
  </div>
</template>

<script setup>
import Account from ‘../components/Account.vue‘
import { store } from ‘../store‘

if (!store.state.user) {
  // Redirect to login if not signed in   
  window.location.href = ‘/login‘
}
</script>

The dashboard and account pages first check if there‘s an authenticated user and redirects to the login page if not.

Enable magic link authentication

In addition to email/password auth, Supabase also supports passwordless "magic link" login. When a user enters their email, Supabase sends them a login link.

To enable magic links, go to Authentication > Settings in the Supabase dashboard and enter your site‘s URL in the "Site URL" field. This is the URL that the magic link will redirect to after a successful login.

Set site URL

Next, create a MagicLink.vue component:

<template>
  <form @submit.prevent="sendMagicLink">
    <h2>Sign In with Magic Link</h2>
    <div>
      <label for="email">Email</label>
      <input type="email" id="email" v-model="email" required />
    </div>
    <button type="submit">Send Magic Link</button>
  </form>
</template>

<script setup>
import { ref } from ‘vue‘
import { supabase } from ‘../supabase‘

const email = ref(‘‘)

async function sendMagicLink() {
  try {
    const { user, error } = await supabase.auth.signIn({
      email: email.value,
    })
    if (error) throw error
    alert(‘Check your email for the magic link!‘)
  } catch (error) {
    alert(error.message)
  }
}
</script>

Instead of passing both email and password to signIn, it only needs the email. Supabase will generate a unique login link and email it to the user.

Finally, update App.vue to show the magic link option:

<RouterLink to="/magic-link" v-if="!user">Magic Link</RouterLink>

And add the corresponding route:

// src/router.js
// ...
{
  path: ‘/magic-link‘,
  component: () => import(‘./views/MagicLink.vue‘),
},

Create the view component:

<!-- src/views/MagicLink.vue -->
<template>
  <div>
    <MagicLink />  
  </div>
</template>

<script setup>
import MagicLink from ‘../components/MagicLink.vue‘
</script>

Now users have the option to authenticate via magic link as well!

Conclusion

In this tutorial, we covered how to add Supabase authentication to a Vue 3 app. We created components for sign up, sign in, sign out, and magic link auth. We also saw how to keep track of the user‘s auth state in a store and conditionally render views based on it.

Supabase makes it easy to add secure, customizable auth to your Vue apps. In addition to email/password and magic links, it also supports social logins, multi-factor auth, and more. Check out the Supabase Auth docs to learn more.

The full code for this tutorial is available on GitHub. Feel free to use it as a starting point for your own Vue apps with Supabase auth!

Similar Posts