How to add custom headers to GraphQL queries?

Is it possible to add custom headers to the GraphQL requests made by the Apollo client?

I’m getting a jwt from the Google API OAuth2 process, and I’d like to add it as a header to all subsequent GraphQL requests. It looks like the Apollo client is configured inside RedwoodProvider, but I can’t figure out a way to update it from within the context.

Hey Bennet,

I released a test version of RedwoodJS over the weekend that integrates our own authentication stack for Netlify Identity, Auth0 and Netlify’s GoTrue API.

I haven’t had time to look into how Google does it, but it may be fairly similar. (Integration is usually a few lines.)

But, I don’t want to lead you down a different path, because we haven’t officially launched it and it looks like we’re not targeting your auth provider (yet.)

You have to remove RedwoodProvider, and instead use GraphQLProvider directly, like we’re doing over here:

I did see that the test version with auth was released. I’d be interested in giving that a shot (this is more of an experimental project anyway). What’s the easiest way to install the release candidates from NPM? Or I’d even be willing to install straight from master - how could I do that while keeping the various sub-repos in sync?

From reading through the way you’re handling the different auth providers, I think Google would fit right in. It looks like I would need to make it conform to the AuthClient interface you’ve designed, which should be very doable. Are you planning to make the AuthClient extensible by us in this way? That would be awesome.

WOOH!

You can install our canary releases (which are released with each merge to master)

In these 3 files, update the @redwoodj/* packages:



And run yarn install

Absolutely! We don’t include any of the dependency libraries for the various clients, so size is not an issue, and I would love to have more and more included!

Once you’ve upgraded your redwood packages, you’ll have access to the redwood-tools command, which will allow you to modify the redwood framework and copy those changes directly into your redwood project, this workflow is described over here: https://github.com/redwoodjs/redwood/blob/master/CONTRIBUTING.md#copy-and-watch

Great thanks! I think I can hook into this to do what I need to.

One thing that I’m still not sure how to accomplish is to sync a 3rd party auth service with my Redwood database. When the service returns the user info, I want to make a GraphQL call to add that user to my Postgres database… but since the AuthProvider is outside of the RedwoodProvider there’s no Apollo context for the mutation. One thing I’ll try is to have an inner AuthProviderHandler context (not sure what to call it) that sits inside the RedwoodProvider and basically just watches for changes to the auth user provided by the outer AuthProvider. When changes are detected it could make fire off a mutation, which would work since it’s inside the RedwoodProvider.

Does that sound reasonable? Any other ideas on how to sync e.g. an Auth0 user with the Redwood database?

So, we handle all of that for you; and this is how it works:

  1. When you render AuthProvider we assign the useAuth hook to window.__REDWOOD__USE_AUTH

window.__REDWOOD__USE_AUTH = useAuth

  1. This window.__REDWOOD__USE_AUTH reference is the default value to RedwoodProvider. Redwood Provider has some code that figures out your authentication state, and automatically passes the jwt to each request. It also passes along some metadata about the type of auth client you’re using.
  1. When the “api side” picks up this metadata is uses this to verify and decode the jwt, which will contain the sub, this is passed into the context as currentUser, which you can use as a stable reference to a user.

You can see the currentUser used in the api side in the example-invoice app: https://github.com/redwoodjs/example-invoice/blob/f4699bdcb1cc83b94a8de7050d39a6bb655962b5/api/src/services/invoice.ts#L5

I think I misunderstood your question. If you want to create / or update a user you could do something after they’ve logged in;

const LoginComponent = () => {
const { login, currentUser } = useAuth()

    return <Button onClick={async () => {
       await login()
       const user = await currentUser()
      // fire off mutation.
      // redirect.
}}
} />

I’ll mention this in our team meeting tomorrow night and figure out if we can come up with something a bit nicer than this.

Excited to see this conversation happening!

@bennett you trying this out is a huge help to us. So keep the feedback and questions coming. One idea for later if applicable --> if you are able to successfully integrate Google Auth, it would be :star_struck::rocket: if you could create a new topic with the rough outline of a “how to” for others to try. No pressure either way.

:crossed_fingers:

I feel like I’m close to getting this working, but I’m running into a roadblock.

With the Google Auth process, you can request “offline access”, which means that you get a refresh token for making API calls from your server on behalf of the user. This is what I need to do.

The issue I’m running into is that this flow requires making a request from my server with the authorization code that the client gets back after a user grants permission. This means I need to make a request in the Auth provider, from the web side to the api side, to pass along this code. But I’m outside of the Redwood/GraphQL context so there is no GraphQL client defined.

The flow looks like:

  • The user clicks “login” and approves my app for certain API scopes
  • The client gets an authorization code from Google in the callback
  • The client passes this code to the server (<-- here’s my problem)
  • The server sends the code and the client_secret to Google and receives tokens (access_token, refresh_token, id_token).
  • The server stores the refresh_token in the database to make future requests for user data
  • The server decodes the id_token to get the user info and returns this to the client
  • The client can then save the user info (including a jwt) to keep the user logged in, and make authenticated requests to the Redwood API

I think the reason this isn’t a problem with the other AuthClients baked into Redwood is that they don’t depend on the Redwood server to complete the auth flow. So your suggestion to get the user object after completing login would work great for them. But for Google offline access, login isn’t really complete until I can make one GraphQL request inside the AuthProvider to get the user info… but the GraphQL context is created in the RedwoodProvider, and so is inaccessible to the AuthProvider.

It seems like if I could create a temporary apolloClient inside the AuthProvider just for this one request I could pull this off. So far I haven’t figured out how to do that without a GraphQL context. If you’ve got any ideas I’d love to hear them!

Welp, I did just what I said and I think it works.

If I do import {createGraphQLClient, gql } from '@redwoodjs/web', then I can use those inside my custom GoogleAuthProvider to send a one-off mutation to the server with the authorization code. This lets me complete the whole flow from inside the provider.

Once I can confirm this is all working I’ll create a sample project to share.

1 Like

Nice work here, Bennett! I’ll admit, I was reading the first comment and starting assuming the worst. Excited to read the follow-up.

And if you have the time to share the sample project, that would be fantastic!

Once I can confirm this is all working I’ll create a sample project to share.

Awesome, thanks so much for this, it’s great to get some other providers with different implementation details!

I think the route that you’re taking would be fairly common for things that need an offline/ “refresh token” - we’ll make a happy path for people who need this!

Ok, this appears to be working, at least using the dev server.

I had to hack together my own version of getUserFromContext in the graphql function, but otherwise it seems to be pretty straightforward.

@peterp I would love for you to take a look at the repo and see if you have any thoughts or suggestions (link below). Specifically, if you can look at the GoogleAuthContext.js file, as well as how I’m storing the user and profile in the User services.

I also tried to get it deployed to Netlify, but the graphql function is throwing an error like "Error: Cannot find module '@babel/runtime-corejs3/helpers/interopRequireWildcard'. Any thoughts on what that’s about?

2 Likes

@bennett would you be able to share the link to your Netlify Build log? For public project the logs are publicly viewable. But do make sure there’s nothing sensitive included. And understood either way.

Neat, I didn’t know Netlify build logs were public for public repos.

Here you go:
https://app.netlify.com/sites/redwood-googleauth-4df3a6/deploys/5ebb5f6695b7c468e0592bc4

Hmm, that doesn’t look like the full deploy log. And I’m not seeing the error.

Do you have the Beta Builds enabled for this project? Screenshot from top right corner of my account:
Screen Shot 2020-05-12 at 9.06.26 PM

You’ll see something like this in your build log if you do

9:08:29 PM: ​
9:08:29 PM: ┌─────────────────────────────┐
9:08:29 PM: │        Netlify Build        │
9:08:29 PM: └─────────────────────────────┘
9:08:29 PM: ​

Here’s a log example of mine for reference:
https://app.netlify.com/sites/redwoodjs-tutorial-test/deploys/5ebb728066e83c59d8bda2d6

Edit:
It was the beta builds setting! I had that enabled on another project, but for some reason I assumed it was then enabled for all projects. Once I turned that on it worked fine!

Original:
Ah, I think my earlier message was confusing. That error isn’t happening during the build - the build completes without a problem. This error happens on the API side when completing the auth process. But when running on the dev server there are no errors.

1 Like

Ok! Thanks for the heads up - I’m finishing off the last bit of the auth work (over here: https://github.com/redwoodjs/redwood/pull/536) and then I’ll jump in over here! Super excited to get this added as an official auth provider!

Here is a working deploy of this project. Right now it does nothing but let you sign in with a Google account (so it asks for the profile, email, and openid scopes). If it would be helpful I can include making an API call on behalf of that user using the stored refresh token.

https://redwood-googleauth-4df3a6.netlify.app/

3 Likes