graphql / graphiql

GraphiQL & the GraphQL LSP Reference Ecosystem for building browser & IDE tools.
MIT License
16.1k stars 1.73k forks source link

How to use Graphiql when /graphql protected by JWT token (authorization header) #59

Closed sulliwane closed 4 years ago

sulliwane commented 9 years ago

My /graphql route is protected by JWT token, so every HTTP request needs to set:

 headers: {
      Authorization: 'Bearer ' + token
    }

To get through the authentication middleware and hit /graphql.

How to manage this authentication step when using graphiql ? graphiql is so convenient, it's a pity to not use it :( thanks for any suggestion!

Neitsch commented 5 years ago

@khaledosman - thank you for your input. Would you mind help me understand it in some more detail? Are you looking for an explicit method for setting the HTTP headers? At this point I believe you can do something like this:

// This is where you can choose what auth headers to add
function headerGenerator() {
  return {
    'Authorization': 'Bearer <your token goes here>',
  }
}

// Semi generic GraphQL query execution method
function graphQLFetcher(additionalHeadersFunction) {
  return function(graphQLParams) {
    return fetch('/graphql', {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        ...additionalHeadersFunction()
      },
      body: JSON.stringify(graphQLParams),
      credentials: 'include',
    }).then(function(response) {
      return JSON.parse(response.text());
    });
  }
}

ReactDOM.render(
  React.createElement(GraphiQL, {
    fetcher: graphQLFetcher(headerGenerator),
  }),
  document.getElementById('graphiql')
);

This level of effort appears relatively low to me. Would you mind elaborating what you would like to see? Am I missing anything that this solution fundamentally does not deliver? Would you like to see this as a setup in the example package of the repo?

khaledosman commented 5 years ago

@Neitsch a setup example/readme would be a good start.

I think most people use the graphiql middleware and don't setup/use the react component manually, so I'd expect to bypass the component configuration all together and have some form for the headers rendered as part of the component or hidden behind a button that takes care of this by default.

Neitsch commented 5 years ago

@khaledosman - ah, apologies if I missed that before. That sounds pretty reasonable, especially small setups don't want to go through the trouble of setting all that up. I briefly looked into this. The key challenge is that express-graphql basically only gives you server side configuration options. There are a few things we could attempt:

Neither solution is particularly good, but that's the only two I can come up with right now. I'll keep thinking about it some more. Thanks again for the input!

khaledosman commented 5 years ago

For some inspiration this is how prisma's playground does it which is built on top of graphiql afaik. I can just enter a JSON object in the headers field which gets sent to the server. The new Apollo Server v2 now includes playground by default instead of graphiql.

image

aamod1990 commented 4 years ago

My /graphql route is protected by JWT token, so every HTTP request needs to set:

 headers: {
      Authorization: 'Bearer ' + token
    }

To get through the authentication middleware and hit /graphql.

How to manage this authentication step when using graphiql ? graphiql is so convenient, it's a pity to not use it :( thanks for any suggestion!

Currently we are using Modify Headers add ons to pass the token in our graphql

acao commented 4 years ago

Just specify that header when creating your required fetcher!

rs-ds commented 4 years ago

For firefox users, here is the Modify Header Value addon: https://addons.mozilla.org/en-US/firefox/addon/modify-header-value/?src=search

gsugambit commented 4 years ago

I definitely think it's easier to use something like mod header to solve this problem. It's avaiable in chrome or firefox

acao commented 4 years ago

we will be providing an example soon, but all of this is possible with the current GraphiQL

penx commented 4 years ago

@acao is this via the plugin system (in v1.0.0-alpha?) previously mentioned by @benjie ?

In GraphiQL v1 we're planning to add a plugin system

acao commented 4 years ago

the plugin system will make it easier to provide a nice UI for this. there are going to be quite a few more alpha versions before we have a plugin system

this is all possible currently though, in 0.7.5 or the alpha, so i had hoped to provide a full auth example before I got too deep into working on monaco or the react refactor for plugins

hassaantariq50 commented 4 years ago

I found a way to fix this problem. On Chrome, you can install an extention ModHeader https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj which will allow you to modify request or response headers. In this way, you can send Authorization header with each request and graphiql will work fine.

how to use it? please guide me

luisdav86 commented 4 years ago

I used this chrome extentions thata allows to send headers https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja

mrceperka commented 4 years ago

This one works for me :) image

acao commented 4 years ago

I've decided - given that we are doing one final 1.0.0 (pre rewrite/plugins) stable release for GraphiQL, we get to have one feature.

And that's a headers tab. Whoever wants to, feel free to add a single tab for headers in a PR, and if we can get it merged next week, we can at least have that until we have a stable beta plugin API!

connorshea commented 4 years ago

I've opened a draft PR for this :) #1543

harshithpabbati commented 4 years ago

As header tab is added to GraphiQL now we can close this issue right @acao?

connorshea commented 4 years ago

Yep, if you upgrade to 1.0.0 you can enable the headers editor :D I have it enabled on my app here.

The code I use to create the GraphiQL instance with the headers editor enabled and pre-filled is this:

let graphQLFetcher = (graphQLParams, opts = { headers: {} }) => {
  return fetch(window.location.origin + '/graphql', {
    method: 'post',
    headers: Object.assign({ 'Content-Type': 'application/json', 'User-Agent': 'vglist.co GraphiQL' }, opts.headers),
    body: JSON.stringify(graphQLParams),
  }).then(response => response.json());
}

ReactDOM.render(
  React.createElement(GraphiQL, {
    fetcher: graphQLFetcher,
    headerEditorEnabled: true,
    headers: '{\n  "X-User-Email": "foo@example.com",\n  "X-User-Token": "API_TOKEN_HERE"\n}'
  }),
  document.getElementById("graphiql-injection-point")
);

We should probably update the README to mention give a code example for using the headers editor and sending headers with the GraphiQL requests.

acao commented 4 years ago

@connorshea excellent point, yes.

possibly we can add custom headers to the graphiql cdn exmaple?

jonaskello commented 4 years ago

The headerEditorEnabled is great :-). Is there also some way to include those headers when graphiql asks for the schema? It seems like graphQLFetcher is also used for fetching the schema but in that case opts have empty headers (at least from my limited testing).

acao commented 4 years ago

I'm deep into 2.0.0 plugin API now, but possibly @connorshea can add that for another 1.0.0 release? I should have caught that in the review, my bad.

acao commented 4 years ago

to confirm, @jonaskello it was not added. i will pivot to fix this issue!

acao commented 4 years ago

released #1593 as graphiql@1.03, enjoy!

connorshea commented 4 years ago

šŸ‘ @acao I think we can close this now :)

jonaskello commented 4 years ago

@acao I can confirm it works! Thanks :-)

acao commented 4 years ago

thanks everyone!

for the record, and you can see this in the GraphiQL readme now, you can enable the headers editor with headersEditorEnabled prop, and even use a static headers string prop to set the default headers in the headers editor, and users can override this per-request in the headers tab.

thanks @connorshea for your work and @harshithpabbati and @ncthbrt for helping to review!

any new features for this will be part of graphiql@2.0.0 plugins, as, following the Spec WG, HTTP-driven functionality is now part of a seperate GraphQL HTTP Spec. We must honor the HTTP agnostic default spec by default, and then will have http plugins to follow the HTTP spec.

consider this a bonus for the very long feature freeze as we had to spend so long preparing GraphiQL for the rewrite, performing the rewrite, and are now working on the plugin spec. the new SDK-oriented approach will be very rewarding!

acao commented 4 years ago

to add, if there are any more bugs with this new feature, we will gladly introduce them to 1.0.x patches :)

jcarroll2007 commented 4 years ago

for the record, and you can see this in the GraphiQL readme now,

@acao I'm not seeing this in the readme. Would you possibly mind sharing a link to docs on how to use this feature?

Found it! https://github.com/graphql/graphiql/blob/241b81ddf8b8fe2fee23eb8674b782ae53314def/packages/graphiql/README.md#props

penx commented 4 years ago

for the record, and you can see this in the GraphiQL readme now, you can enable the headers editor with headersEditorEnabled prop

@acao you may want to edit this to save people time trying to figure out why it doesn't work - it's headerEditorEnabled not headersEditorEnabled

(edit - shameless plug - I just made this, feedback welcomed https://github.com/penx/graphiql-middleware )

acao commented 4 years ago

@penx thanks for the catch, will update!

your middleware looks great but it exposes a huge template injection attack surface - exposing xss and other potential attack vectors! i had to issue a CVE for graphql playground and issue a security fix for the same exact reasons. playground repo now has an example with a readme that explains all of this

we will be rolling our own middeware packages soon when i have time to get to it. there is still an open PR that needs cleanup if you want to create a new PR based off that to help advance that! We actually use ReactDOM/server in that PR there, but an xss filtered template tag implementation ala the new playground fix would be welcome as well. you can introduce several new typescript packages if you want, or just a basic underling renderer method like playground-html

craigmit commented 3 years ago

Easy implementation to get JWT Token support in GraphiQL

https://gist.github.com/sasso/3c3d728e0049d5b66a2c19b349b7f164

screenshot 2017-07-07 09 46 20

This worked great, except the graphiql screen falls off the bottom of the page.

Here's a version using flexbox that aligns it properly: https://gist.github.com/craigmit/44499818664fc34083f2aa96069f0636

benjie commented 3 years ago

You could also use the headers editor:

Screenshot_20210218_145143

craigmit commented 3 years ago

Thanks @benjie

I didn't see that Request Headers were now available.

I modified the new GraphiQL html, to automatically set the auth header from a # param (so you can call it directly from your application, automatically setting the Authorization header):

https://gist.github.com/craigmit/0cce78ffe33ca3551fbcc35016e8b3e2

Usage is:

graphiqlSetAuth.html#[JWT_TOKEN]

and it will set the Authorization header for you.

The only change to the original graphiql html was the addition of:

const tokenParam = window.location.hash.substr(1);

if (tokenParam) {
  headers = {"Authorization": "Bearer " + tokenParam}
  window.history.replaceState({}, document.title, window.location.href.split('#')[0]);
}
njlr commented 3 years ago

The request headers box works a treat!

const editor = (<GraphiQL headerEditorEnabled={true} fetcher={graphQLFetcher} />);

ReactDOM.render(editor, document.getElementById('graphiql-app'));

However, you must pass the headers through in your fetcher.

For example:

const graphQLFetcher = async (graphQLParams, opts) => {
  const { headers = {} } = opts;

  const response = await fetch('/graphql', {
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      ...headers,
    },
    body: JSON.stringify(graphQLParams),
  });

  return response.json();
};
m-thirumal commented 2 years ago

Screen Shot 2022-09-23 at 6 57 08 PM Add Authentication in the UI itself

cedricDevWP commented 2 years ago

@m-thirumal hello, i try to configure api platform with jwt and graphql but I canā€™t do itā€¦ all the time I have 403 when I use security grantedā€¦

can you explain how have you configure security.yaml (and other file maybe) to make work pls šŸ˜• ?

Akifcan commented 1 year ago
graphqlHTTP({
    schema,
    rootValue,
    graphiql: {
      headerEditorEnabled: true
    }
  })
KenEucker commented 1 year ago

I have been digging through this thread trying to find a way to pass a header, the authorization header, to Graphiql in the request. I am running Graphiql in an iFrame from localhost:8000/graphql and I want to be able to do something like localhost:8000/graphql?headers=serializedheadersstring and have the header values be populated into the editor for all tabs.

Is this possible?

njlr commented 1 year ago

I have been digging through this thread trying to find a way to pass a header, the authorization header, to Graphiql in the request. I am running Graphiql in an iFrame from localhost:8000/graphql and I want to be able to do something like localhost:8000/graphql?headers=serializedheadersstring and have the header values be populated into the editor for all tabs.

Is this possible?

Not a solution but a work-around. You can run a local proxy that puts the GraphiQL UI and the GraphQL endpoint on the same port then the headers are shared automatically.

koistya commented 1 year ago

You can inject the following code snippet to make GraphiQL IDE work with Firebase Auth (ID token):

<script type="module">
  import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
  import { getAuth } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";

  const app = initializeApp({
    projectId: "example",
    appId: "xxxxx",
    apiKey: "xxxxx",
    authDomain: "example.com"
  });

  function setAuthHeader(token) {
    const editor = document.querySelectorAll('.variable-editor .CodeMirror')[1].CodeMirror;
    const headers = JSON.parse(editor.getValue());
    headers.Authorization = token ? "Bearer " + token : undefined;
    editor.setValue(JSON.stringify(headers, null, 2));
  }

  getAuth(app).onAuthStateChanged((user) => {
    if (user) {
      user.getIdToken().then(token => setAuthHeader(token));
    } else {
      setAuthHeader(null);
    }
  });
</script>

https://www.codementor.io/@koistya/graphiql-with-firebase-auth-251hx5qhz3

240006671-eb7c1db1-f6a8-42bb-9392-5161722ba559