0

I have a vue web app from which I'm trying to run a subscription using a hasura query.

My problem is that I cannot pass to the Websocket request an authorization token as the backend expects. These are my current settings:

const token = localStorage.getItem("token") || null;

const options = {
  httpUri: //graphql http entpoint,
  wsUri: //graphql ws endpoint
};
let link = new HttpLink({
  uri: options.httpUri
});

// Create the subscription websocket link if available
if (options.wsUri) {
  const wsLink = new WebSocketLink(
    new SubscriptionClient(options.wsUri, {
      lazy: true,
      reconnect: true,
      connectionParams: {
        headers: {
          Authorization: `Bearer ${token}`
        }
      }
    })
  );

  // using the ability to split links, you can send data to each link
  // depending on what kind of operation is being sent
  link = split(
    // split based on operation type
    ({ query }) => {
      const definition = getMainDefinition(query);
      return (
        definition.kind === "OperationDefinition" &&
        definition.operation === "subscription"
      );
    },
    wsLink,
    link
  );
}

const authLink = setContext((_, { headers }) => {
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : ""
    }
  };
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.map(({ message }) => {
      if (message.includes("unauthorized")) {
        EventBus.$emit("unauthorized");
      }
    });

  if (networkError) console.log(`[Network error]: ${networkError}`);
});

const apolloClient = new ApolloClient({
  link: ApolloLink.from([errorLink, authLink, link]),
  cache: new InMemoryCache(),
  connectToDevTools: true
});

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
});

When I try to run the subscription I get

HTTP Authentication failed; no valid credentials available

And in the ws request header I cannot see my Authorization bearer set.

A side info I need authorization for both http and ws requests

Andrei Maieras
  • 696
  • 5
  • 15
  • 34

1 Answers1

1

I think errorLink or authLink unexpectedly change websocket header token. You try modifying a bit:

    const httpLink = from([
      authLink,
      // errorLink,
      new HttpLink({
        uri: Config.httpDataHost,
        headers: {
          [XHasuraClientName]: Config.hasuraClientName
        }
      })
    ]);
    
    const wsLink = new WebSocketLink({ ... });
    const link = ...
    const apolloClient = new ApolloClient({
      link: ApolloLink.from([errorLink, link]),
      cache: new InMemoryCache(),
      connectToDevTools: true
    });

If it doesn't work, you can try commenting errorLink to check. Another thing is, you shouldn't get token globally, but use lazy function so ApolloClient can always get latest access token from local storage

const getIdToken = () => localStorage.getItem('token') || null;

const wsLink = new WebSocketLink({
  uri: options.wsUri,
  options: {
    connectionParams: () => ({
      headers: {
        Authorization: getIdToken(),
      }
    }),
    ...
  }
});

PS: I have an example repository with React + Apollo Client 3.0. Although you are using Vue.js,Apollo Client construction is the same https://github.com/hgiasac/ra-hasura-typescript-boilerplate/blob/auth-jwt/src/shared/ApolloClient.ts

hgiasac
  • 2,183
  • 16
  • 14
  • I tried commenting the `erroLink` , but still no headers sent to the ws request, the `connectionParams` function is never called – Andrei Maieras Jun 28 '20 at 17:19
  • do you run subscription after authentication? Subscription won't run until first subscription call with `lazy` option – hgiasac Jun 29 '20 at 09:32
  • Yes, I found the problem - I am using the hasura engine inside docker, and nginx as reverse proxy to that hasura instance, so I had to create a route in nginx config file to point to the inner docker hasura engine, but your github example really helped, thanks a lot for this – Andrei Maieras Jun 29 '20 at 13:11