Vue-apollo: Authorization Header via `getAuth`

Created on 29 Nov 2019  Â·  7Comments  Â·  Source: vuejs/vue-apollo

Is your feature request related to a problem? Please describe.
Yes, it's a problem but this may have to do with my lack of understanding or choice of implementation details.

Our team is using Vue Apollo as the interface to our Apollo Graph QL server. For authentication, we are using Okta's (OIDC/OAuth) PKCE workflow for single sign on. What I have attempted but fail to do is to find a way to use the vue-apollo client's getAuth callback to set the Authorization token to: `BEARER .

Describe the solution you'd like
I'd like to use the getAuth callback to override the default with the HTTP Authorization header as follows so that our Apollo API gateway can extract just the string and do its own validation and authorization.
Authorization: `BEARER .

const defaultOptions: ApolloClientClientConfig = {
httpEndpoint, ...
// Override the way the Authorization header is set
getAuth: (tokenName: string) => {
Vue.prototype.$auth.getAccessToken()
.then((accessToken: string) => {
return Bearer ${accessToken}
})
}

As you can see the OKTA Auth library code (at: https://github.com/okta/okta-oidc-js/blob/master/packages/okta-vue/src/Auth.js) is written with async/await and returns a promise. What I have failed to do is to find a way for the the getAuth to wait for the OKTA's getAcessToken() promise to resolve. Is there a way to force the Apollo Client to wait for the promise to resolve?

Describe alternatives you've considered
It appears as though I am stuck because I don't know how to integrate an async function I need by Okta to work with getAuth which expects a synchronous response.

  1. As I've written the code, I always return the promise but it's ignored by the Apollo Client. I've tried setting the AUTH_TOKEN as follows:
    const AUTH_TOKEN = 'okta-token-storage'
    This is problematic, because OKTA's token is an object not a string and contains too much info that I don't want to pass through as HTTP headers. It contains name, email, profile and other related info required by the Vue app but is not allowed per our InfoSec team.
  2. I've tried adding async to convert the signature of the function
    getAuth: async (tokenName: string) => {
    This also falils
    Additional context
enhancement

Most helpful comment

For @CVANCGCG and anyone else who's ended up here from Google etc, this is how I got it to work…

  1. Create a new auth link:
import { setContext } from "apollo-link-context";

const authLink = setContext(async (_, { headers }) => {
  // Use your async token function here:
  const token = await getToken();
  // Return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : ""
    }
  };
});
  1. Add it to your defaultOptions object:
const defaultOptions = {
  // ...
  link: authLink
  // ...
}

Hope that makes sense, let me know if you have any questions!

All 7 comments

@CVANCGCG usually I do this sort of thing through the Apollo-link-context, which definitely supports async/await. Is there a reason you can’t do that?

Hi Paul, thank you for the prompt response. Is this the best reference issue to go over for Apollo-link-context. I ask because we have code that uses these initializers (below) that uses "options" objects and properties, rather than the functions referenced in the link above. In our code base, it's harder to determine where to apply the HTTP headers.

```// Call this in the Vue app file
export function createProvider(options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options,
cache: new InMemoryCache({
fragmentMatcher,
}),
})

// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
// fetchPolicy: 'cache-and-network',
},
},
errorHandler(error) {
// eslint-disable-next-line no-console
console.log(
'%cError',
'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;',
error.message
)
},
})
apolloProvider['wsClient'] = wsClient
```

For @CVANCGCG and anyone else who's ended up here from Google etc, this is how I got it to work…

  1. Create a new auth link:
import { setContext } from "apollo-link-context";

const authLink = setContext(async (_, { headers }) => {
  // Use your async token function here:
  const token = await getToken();
  // Return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : ""
    }
  };
});
  1. Add it to your defaultOptions object:
const defaultOptions = {
  // ...
  link: authLink
  // ...
}

Hope that makes sense, let me know if you have any questions!

@Booligoosh I have the same link setup for auth tokens but they don't get sent to the websocket endopoint, only http. Any ideas on how to fix that?

@wiemann Not sure sorry, I only use an HTTP endpoint in my app.

@Booligoosh I have the same link setup for auth tokens but they don't get sent to the websocket endopoint, only http. Any ideas on how to fix that?

Websockets don't support headers in browsers. You have to do the configuration via WebSocketLink.

@Booligoosh , thanks so much for the response and apologize for the delayed response. You are absolutely correct that the setContext() callback is called per request and is working for me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anymost picture anymost  Â·  3Comments

alx13 picture alx13  Â·  4Comments

igaloly picture igaloly  Â·  3Comments

mathe42 picture mathe42  Â·  4Comments

danthareja picture danthareja  Â·  4Comments