Home > OS >  Why does my Auth0 Vue 3 setup throw TypeScript errors?
Why does my Auth0 Vue 3 setup throw TypeScript errors?

Time:10-21

I found this this repo that creates an Auth0-spa-js TypeScript plugin for Vue 2, and I am using it inside my Vue 3 project.

But the problem is that when using functions like auth.loginWithRedirect(); and auth.logout(); it throws a TypeScript error saying Object is of type 'unknown'. ts(2571).

I'm new to TypeScipt and not sure what I am doing wrong.

Here is a link to my Vue 3 repo that is throwing the errors. And the essential parts of the code throwing the errors is also copied below.

This is what the Auth0 plugin looks like:

import createAuth0Client, {
  Auth0Client,
  GetIdTokenClaimsOptions,
  GetTokenSilentlyOptions,
  GetTokenWithPopupOptions,
  LogoutOptions,
  RedirectLoginOptions,
  User,
} from "@auth0/auth0-spa-js";
import { App, Plugin, computed, reactive, watchEffect } from "vue";
import { NavigationGuardWithThis } from "vue-router";

let client: Auth0Client;

interface Auth0PluginState {
  loading: boolean;
  isAuthenticated: boolean;
  user: User | undefined;
  popupOpen: boolean;
  error: any;
}

const state = reactive<Auth0PluginState>({
  loading: true,
  isAuthenticated: false,
  user: {},
  popupOpen: false,
  error: null,
});

async function handleRedirectCallback() {
  state.loading = true;

  try {
    await client.handleRedirectCallback();
    state.user = await client.getUser();
    state.isAuthenticated = true;
  } catch (e) {
    state.error = e;
  } finally {
    state.loading = false;
  }
}

function loginWithRedirect(o: RedirectLoginOptions) {
  return client.loginWithRedirect(o);
}

function getIdTokenClaims(o: GetIdTokenClaimsOptions) {
  return client.getIdTokenClaims(o);
}

function getTokenSilently(o: GetTokenSilentlyOptions) {
  return client.getTokenSilently(o);
}

function getTokenWithPopup(o: GetTokenWithPopupOptions) {
  return client.getTokenWithPopup(o);
}

function logout(o: LogoutOptions) {
  return client.logout(o);
}

const authPlugin = {
  isAuthenticated: computed(() => state.isAuthenticated),
  loading: computed(() => state.loading),
  user: computed(() => state.user),
  getIdTokenClaims,
  getTokenSilently,
  getTokenWithPopup,
  handleRedirectCallback,
  loginWithRedirect,
  logout,
};

const routeGuard: NavigationGuardWithThis<undefined> = (
  to: any,
  from: any,
  next: any
) => {
  const { isAuthenticated, loading, loginWithRedirect } = authPlugin;

  const verify = async () => {
    // If the user is authenticated, continue with the route
    if (isAuthenticated.value) {
      return next();
    }

    // Otherwise, log in
    await loginWithRedirect({ appState: { targetUrl: to.fullPath } });
  };

  // If loading has already finished, check our auth state using `fn()`
  if (!loading.value) {
    return verify();
  }

  // Watch for the loading property to change before we check isAuthenticated
  watchEffect(() => {
    if (!loading.value) {
      return verify();
    }
  });
};

interface Auth0PluginOptions {
  domain: string;
  clientId: string;
  audience: string;
  redirectUri: string;

  onRedirectCallback(appState: any): void;
}

async function init(options: Auth0PluginOptions): Promise<Plugin> {
  client = await createAuth0Client({
    // domain: process.env.VUE_APP_AUTH0_DOMAIN,
    // client_id: process.env.VUE_APP_AUTH0_CLIENT_KEY,
    domain: options.domain,
    client_id: options.clientId,
    audience: options.audience,
    redirect_uri: options.redirectUri,
  });

  try {
    // If the user is returning to the app after authentication
    if (
      window.location.search.includes("code=") &&
      window.location.search.includes("state=")
    ) {
      // handle the redirect and retrieve tokens
      const { appState } = await client.handleRedirectCallback();

      // Notify subscribers that the redirect callback has happened, passing the appState
      // (useful for retrieving any pre-authentication state)
      options.onRedirectCallback(appState);
    }
  } catch (e) {
    state.error = e;
  } finally {
    // Initialize our internal authentication state
    state.isAuthenticated = await client.isAuthenticated();
    state.user = await client.getUser();
    state.loading = false;
  }

  return {
    install: (app: App) => {
      app.provide("Auth", authPlugin);
    },
  };
}

interface Auth0Plugin {
  init(options: Auth0PluginOptions): Promise<Plugin>;
  routeGuard: NavigationGuardWithThis<undefined>;
}

export const Auth0: Auth0Plugin = {
  init,
  routeGuard,
};

And this is what my App.vue file looks like (the errors are showing on this file):

<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/profile">Profile</router-link> |
    <router-link to="/faunaapi">API</router-link> |
    <button @click.prevent="login">LOGIN</button> |
    <button @click.prevent="logout">LOGOUT</button>
  </div>
  <router-view />
</template>

<script lang="ts">
import { defineComponent, inject } from "vue";
export default defineComponent({
  name: "App",
  setup() {
    const auth = inject("Auth");
    const login = (): void => {
      auth.loginWithRedirect(); //<-- ERROR: Object is of type 'unknown'.
    };
    const logout = (): void => {
      auth.logout({
        returnTo: window.location.origin,
      }); //<-- ERROR: Object is of type 'unknown'.
    };
    return {
      logout,
      login,
      ...auth,
    };
  },
});
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

Do you know what I am doing wrong?

CodePudding user response:

So if you hover with your mouse over "inject" you'll see something like:

(alias) inject<unknown>(key: string | InjectionKey<unknown>): unknown

Specially here inject<unknown>, whats inside < > that's a Generic Type.

Briefly you need to tell to Inject what type is whatever you are injecting, so it'll infer it.

Try this:

Solution 1 - Pass Type Arguments (recomended):

import { Auth0Client } from "@auth0/auth0-spa-js";
import { defineComponent, inject } from "vue";
export default defineComponent({
  name: "App",
  setup() {
    /* Here you pass Auth0Client types, the error should go away. 
    You should get type validation and autocompletion for `auth` now */
    const auth = inject<Auth0Client>("Auth")!; 

Solution 2 - Cast inject type

// Here you are basically saying "Hey whatever inject returns, is an Auth0Client."
const auth = inject("Auth") as Auth0Client;

Hopefully that works :)!

  • Related