Use Vue.js plugin in js service module

风格不统一 提交于 2020-04-17 20:29:31

问题


I am creating an $auth plugin that works fine from the component using this.$auth

main.js

import { Auth0Plugin } from "./auth";

Vue.use(Auth0Plugin, {
  domain: process.env.VUE_APP_AUTH0_DOMAIN,
  clientId: process.env.VUE_APP_AUTH0_CLIENT_ID,
  audience: process.env.VUE_APP_AUTH0_AUDIENCE,
  onRedirectCallback: () => {
    router.push("/signed-in");
  }
});

auth/index.js

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

/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = () =>
  window.history.replaceState({}, document.title, window.location.pathname);

let instance;

/** Returns the current instance of the SDK */
export const getInstance = () => instance;

/** Creates an instance of the Auth0 SDK. If one has already been created, it returns that instance */
export const useAuth0 = ({
  onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
  redirectUri = window.location.origin,
  ...options
}) => {
  if (instance) return instance;

  // The 'instance' is simply a Vue object
  instance = new Vue({
    data() {
      return {
        loading: true,
        isAuthenticated: false,
        user: {},
        auth0Client: null,
        popupOpen: false,
        error: null
      };
    },
    methods: {
      /** Returns the access token. If the token is invalid or missing, a new one is retrieved */
      getTokenSilently(o) {
        return this.auth0Client.getTokenSilently(o);
      },
    },
    /** Use this lifecycle method to instantiate the SDK client */
    async created() {
      // Create a new instance of the SDK client using members of the given options object
      this.auth0Client = await createAuth0Client({
        domain: options.domain,
        client_id: options.clientId,
        audience: options.audience,
        redirect_uri: 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 this.auth0Client.handleRedirectCallback();

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

  return instance;
};

// Create a simple Vue plugin to expose the wrapper object throughout the application
export const Auth0Plugin = {
  install(Vue, options) {
    Vue.prototype.$auth = useAuth0(options);
  }
};

I have a service class that calls an api. I need to get the api token from this plugin $auth.getTokenSilently(). How do I call the instantiated plugin from my api-service.js file?

I know the below code doesn't work. But it's just to demonstrate what I'm trying to do. utils/api-service.js

export default {
  init() {
    this.lqdApi = axios.create({
      baseURL: process.env.lqdApiBaseUrl,
      headers: { Authorization: `Bearer ${$auth.getTokenSilently()}` }
    });
    return this;
  },
}


回答1:


The service should be initialized inside Vue. It can just be injected with initialization data:

  init(token) {
    this.lqdApi = axios.create({
      baseURL: process.env.lqdApiBaseUrl,
      headers: { Authorization: `Bearer ${token}` }
    });
    return this;
  },

The problem here is that async created is a potential antipattern, it doesn't prevent child components from being initialized when auth0Client and therefore api-service aren't ready but used by components. If this is the case, one of possible solutions is to make Axios instance available on Vue prototype, like shown here. The instance needs to be created immediately, but a token can be provided to it asynchronously with interceptors because they support promises. It can be written as a plugin and become available on Vue instance like $auth:

  install(Vue, options) {
    Vue.prototype.$axios = axios.create();
  },
  created() {
    if (this.$root === this) {
      this.$axios.interceptors.request.use(async config => {
        let auth0Client = await this.auth0ClientPromise;
        config.headers.Authorization = `Bearer ${auth0Client.getTokenSilently()}`;
        return config;
    }
  }

And a promise of Auth0 client instance should be available on Vue instance in order to be chained by Axios (and probably other things that may depend on it):

  this.auth0ClientPromise = createAuth0Client(...);
  this.auth0Client = await auth0ClientPromise;


来源:https://stackoverflow.com/questions/60710877/use-vue-js-plugin-in-js-service-module

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!