Veselin
Veselin

Reputation: 73

How to implement auth0 with nuxt3

I am trying to add auth0 to my nuxt3 application, but I am having trouble how to approach it. The auth-nuxt module is still not available for nuxt3 and the auth0-spa-js cannot make it work with SSR, I followed this tutorial.

import createAuth0Client from "@auth0/auth0-spa-js";

let auth = await createAuth0Client({
  domain: "dev-......com",
  client_id: "Z0...................0T6I",
  redirect_uri: '<MY_CALLBACK_URL>'
});

export default auth;
import auth from "../store/authfile";
export default defineNuxtRouteMiddleware(async (to, from) => {
    let isAuthenticated = await auth.isAuthenticated();
    if (to.path === "/" && !to?.query?.code) {
        return;
    }
    if (!isAuthenticated) {
        const query = to?.query;
        if (query && query.code && query.state) {
            await auth.handleRedirectCallback();
        } else {
            await auth.loginWithRedirect();
        }
    } else {
        console.log("logged in ", to.path);
    }
    const router = useRouter();
    if (to.path === "/") {
        to.fullPath = "/";
    }
    navigateTo(to.path);
});

500 document is not defined

at getAll (C:\Users\vesel\Desktop\nuxt-app\node_modules\es-cookie\src\es-cookie.js:68:18) at Object.get (C:\Users\vesel\Desktop\nuxt-app\node_modules\es-cookie\src\es-cookie.js:72:12) at Object.get (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:4550:40) at Object.get (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:4585:35) at Auth0Client. (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:5258:45) at step (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:186:23) at Object.next (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:130:20) at C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:107:71 at new Promise () at __awaiter (C:\Users\vesel\Desktop\nuxt-app\node_modules@auth0\auth0-spa-js\dist\lib\auth0-spa-js.cjs.js:89:12)`

Upvotes: 3

Views: 5174

Answers (3)

Zaeem
Zaeem

Reputation: 149

I went with using this module: nuxt-oidc-auth

I had some issues with opaque tokens being returned but I solved it by setting a default audience in my Auth0 settings:

Check out a working example here nuxt3-auth0

Upvotes: 0

Yarik
Yarik

Reputation: 428

After spending few hours trying to upgrade my small project from nuxt 2 to nuxt 3 I was very disappointed that the old auth-next plugin was not working yet(anymore). I tried to integrate @auth0/auth0-vue as Dylan above was suggesting, but I had few issues with session storage.

Then I discovered @supabase/nuxt-auth which is a nuxt3 wrapper for NextAuthJs that works out "of the box" with minimal configuration and supports many other providers as well.

It works on client and server sides.

On the server side I had configured it like this - server/api/auth/[...].ts file:

import { NuxtAuthHandler } from '#auth'
import Auth0Provider from 'next-auth/providers/auth0'

export default NuxtAuthHandler({
  secret: useRuntimeConfig().authSecret,

  providers: [
    // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
    Auth0Provider.default({
      clientId: useRuntimeConfig().auth0.clientId,
      clientSecret: useRuntimeConfig().auth0.clientSecret,
      issuer: useRuntimeConfig().auth0.issuer,
    })
  ]
})

And login page would look like:

<template>
  <div>
    <a v-if="!isAuthenticated" @click="login">
      <slot>Log In</slot>
    </a>
    <a v-else @click="logout">
      <slot>
        Log Out {{ user }}
      </slot>
    </a>
  </div>
</template>

<script lang="ts" setup>

const ses = useSession({ required: false })

const login = () => ses.signIn('auth0')
const logout = () => ses.signOut('auth0')

const isAuthenticated = computed(() => {
  return ses.status.value === 'authenticated'
})

const user = computed(() => {
  return ses.data.value?.user?.name
})

</script>

The only difference here is that to configure Auth0Provider one would need both clientId and clientSecret and it requires server-side, which might not be suitable for SPA or client-only pages.

Upd: regarding server-side session, there is one special case with universal rendering where headers needs to be passed additionally to have session on the first load:

const headers = useRequestHeaders(['cookie']) as HeadersInit
const { pending, data } = useLazyFetch(`${config.public.apiUrl}/data`, { headers })

Upvotes: 1

Dylan
Dylan

Reputation: 106

Auth0 has a Vue3 package that can be configured as a Nuxt3 plugin.

First, follow only the configuration steps and installation steps outlined in their quickstart.

Next, add it as a Nuxt3 plugin, create a new file named auth.ts in the Plugin folder with the following code. Here we instantiate the auth0 as a plugin and add an 'auth' middleware that re-routes authenticated pages. Remember to add the middleware to pages that require them using definePageMeta:

import { createAuth0 } from '@auth0/auth0-vue'

export default defineNuxtPlugin((nuxtApp) => {
  const auth0 = createAuth0({
    domain: 'xxxx.auth0.com',
    client_id: 'xxx',
    redirect_uri: 'http://localhost:3000',
  })

  if (process.client) {
    nuxtApp.vueApp.use(auth0)
  }

  addRouteMiddleware('auth', () => {
    if (process.client) {
      auth0.checkSession()
      if (!auth0.isAuthenticated.value) {
        auth0.loginWithRedirect({
          appState: {
            target: useRoute().path,
          },
        })
      }
    }
  })
})

Now we can create a login button Vue component (Login.vue) that references this plugin. Note, the Login button can only perform auth0 functionality on the client, thus our default is Log In and will refreshes when the isAuthenticated data gets updated.

<template>
  <div>
    <a
      v-if="!isAuthenticated"
      @click="login"
    >
      <slot>Log In</slot>
    </a>
    <a
      v-else
      @click="logout"
    >
      <slot>Log Out</slot>
    </a>
  </div>
</template>

<script lang="ts" setup>
import { useAuth0 } from '@auth0/auth0-vue'

// Composition API
const auth0 = process.client ? useAuth0() : undefined

const isAuthenticated = computed(() => {
  return auth0?.isAuthenticated.value
})

const login = () => {
  auth0?.checkSession()
  if (!auth0?.isAuthenticated.value) {
    auth0?.loginWithRedirect({
      appState: {
        target: useRoute().path,
      },
    })
  }
}

const logout = () => {
  navigateTo('/')
  auth0?.logout()
}
</script>

More functionality for this package can be found at the Github Repo.

Side note: This middleware only runs on the client side, thus you should assert that all pages do not return sensitive information by default. Auth0 describes NextJS serverless architecture authentication here.

Upvotes: 9

Related Questions