graphql / graphiql

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

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

Closed sulliwane closed 4 years ago

sulliwane commented 8 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!

KyleAMathews commented 8 years ago

Download @skevy's https://github.com/skevy/graphiql-app and your header. I have the same setup as you and use this daily.

sulliwane commented 8 years ago

@KyleAMathews awesome, I will give it a shot (my main computer is linux distro though...). thanks

skevy commented 8 years ago

@sulliwane - I'm sure it would compile on Linux - just haven't tried.

sulliwane commented 8 years ago

I just tried npm install && npm start under linux (ubuntu 14.04) and graphiql-app-V0.3.1, but got blank screen, see here: https://github.com/skevy/graphiql-app/issues/10

I close this issue as I have the answer now.

cancan101 commented 8 years ago

Any reason to close this issue and opposed to adding the header editor to the (main) graphiql?

sulliwane commented 8 years ago

No reason, indeed it would be nice if we could set the header directly into graphiql app. (I'm using graphiql-app right now.)

leebyron commented 8 years ago

GraphiQL is generic and doesn't know anything about the network! This is great because you can configure it to use whatever your GraphQL server uses. This is how @skevy was able to build a standalone app version of GraphiQL.

I suggest taking a look at the example - where you see graphQLFetcher is where you can configure your network fetching however you like, in your case by including any authentication details that may be necessary.

Often, when your GraphQL endpoint is behind a authorization feature like this, it is common to expose the GraphiQL tool via a different endpoint on your server.

MarkAndrewJohnson commented 8 years ago

@leebyron I would agree being generic is a reason not to automate everything. I would think, though, that being generic is exactly why it DOES makes sense to have a field allowing custom headers to be set manually by the user!

I'm also unclear as to your last statement. It doesn't avoid the need for auth tokens to be pushed around.

eugenehp commented 8 years ago

@MarkAndrewJohnson @sulliwane @cancan101 @KyleAMathews Guys, it would be interesting to get your opinion on this https://github.com/graphql/express-graphql/issues/101

mzohaibqc commented 7 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.

vitalcode commented 7 years ago

Look at auth-graphiql (https://github.com/vitalcode/auth-graphiql) that wraps GraphiQL providing support for bearer token and arbitrary URL. Great for testing GraphQL services secured with OAuth 2.0.

jasonmorita commented 7 years ago

For those still having this issue, my coworker came up with a cheap solution (in Express in my case). Have your GraphiQL answer at another endpoint as Lee said above and apply basic-auth to that, where you can supply your JWT, add it to the headers and then let express-jwt pick it up in the next middleware.

hunt commented 7 years ago

Actually, I use a simple solution between our GraphQL development process. If you GraphQL server parse a access_token from querystring more than just a http header.

We can pass access_token in query string to the browser's address bar at our GraphiQL page ?access_token=xxx&query=... then GraphiQL will send access_token to req.query

I parse a token with something like express-bearer-token

estaub commented 7 years ago

FWIW... In dev mode only, I've set up my server to save the most recent valid token used for the graphql endpoint, and slap it on any subsequent graphiql-driven queries (where the referer header is graphiql). I just sign into the app and then can use graphiql at will.

iamsaso commented 7 years ago

Easy implementation to get JWT Token support in GraphiQL

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

screenshot 2017-07-07 09 46 20

PetrSnobelt commented 7 years ago

@sasso Thanks, but your old version does not support websockets :-(

emjaksa commented 7 years ago

Hope this helps. How I implimented custom headers for my GraphiQL interface with a express-graphql server.

Modify your server to handle post requests from one endpoint and get requests to the graphiql interface on another.

app.post(
  '/graphql',
  expressGraphQL({
    schema: GraphQLSchema,
    graphiql: false, // Disable graphiql for posts requests
  }),
)

app.get(
  '/graphql',
  expressGraphQL({
    schema: GraphQLSchema,
    graphiql: true, // Enable graphiql for get requests
  }),
)

Basically you need to create a new index.html for your GraphiQL interface and add it to your servers public directory i.e. <public-path>/graphql/index.html. In that file you can modify the fetch function to send any additional headers in your requests. I got my index.html from the graphiql example.

I use webpack and the html-webpack-plugin to bundle the index.html. This allows it to get the correct version of graphql used in my project from the CDN. Below is an example of my webpack config and my ejs template of the index.html.

webpack.config.js

new HtmlWebpackPlugin({
  filename: 'graphql/index.html', // Write the file to <public-path>/graphql/index.html
  inject: false, // Do not inject any of your project assets into the template
  GRAPHQL_VERSION: packageJSON.dependencies.graphql.replace(/[^0-9.]/g, ''), // Get the graphql version from my package.json
  template: 'graphiql.ejs', // path to template
}),

graphiql.ejs

<!--
 *  Copyright (c) Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the license found in the
 *  LICENSE file in the root directory of this source tree.
-->
<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        height: 100%;
        margin: 0;
        width: 100%;
        overflow: hidden;
      }
      #graphiql {
        height: 100vh;
      }
    </style>

    <!--
      This GraphiQL example depends on Promise and fetch, which are available in
      modern browsers, but can be "polyfilled" for older browsers.
      GraphiQL itself depends on React DOM.
      If you do not want to rely on a CDN, you can host these files locally or
      include them directly in your favored resource bunder.
    -->
    <script src="//cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js"></script>
    <script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
    <script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
    <script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>

    <link href="//cdn.jsdelivr.net/npm/graphiql@<%= htmlWebpackPlugin.options.GRAPHQL_VERSION %>/graphiql.css" rel="stylesheet" />
    <script src="//cdn.jsdelivr.net/npm/graphiql@<%= htmlWebpackPlugin.options.GRAPHQL_VERSION %>/graphiql.min.js"></script>
  </head>
  <body>
    <div id="graphiql">Loading...</div>
    <script>

      /**
       * This GraphiQL example illustrates how to use some of GraphiQL's props
       * in order to enable reading and updating the URL parameters, making
       * link sharing of queries a little bit easier.
       *
       * This is only one example of this kind of feature, GraphiQL exposes
       * various React params to enable interesting integrations.
       */

        // Parse the search string to get url parameters.
      var search = window.location.search;
      var parameters = {};
      search.substr(1).split('&').forEach(function (entry) {
        var eq = entry.indexOf('=');
        if (eq >= 0) {
          parameters[decodeURIComponent(entry.slice(0, eq))] =
            decodeURIComponent(entry.slice(eq + 1));
        }
      });

      // if variables was provided, try to format it.
      if (parameters.variables) {
        try {
          parameters.variables =
            JSON.stringify(JSON.parse(parameters.variables), null, 2);
        } catch (e) {
          // Do nothing, we want to display the invalid JSON as a string, rather
          // than present an error.
        }
      }

      // When the query and variables string is edited, update the URL bar so
      // that it can be easily shared
      function onEditQuery(newQuery) {
        parameters.query = newQuery;
        updateURL();
      }

      function onEditVariables(newVariables) {
        parameters.variables = newVariables;
        updateURL();
      }

      function onEditOperationName(newOperationName) {
        parameters.operationName = newOperationName;
        updateURL();
      }

      function updateURL() {
        var newSearch = '?' + Object.keys(parameters).filter(function (key) {
          return Boolean(parameters[key]);
        }).map(function (key) {
          return encodeURIComponent(key) + '=' +
            encodeURIComponent(parameters[key]);
        }).join('&');
        history.replaceState(null, null, newSearch);
      }

      // Defines a GraphQL fetcher using the fetch API. You're not required to
      // use fetch, and could instead implement graphQLFetcher however you like,
      // as long as it returns a Promise or Observable.
      function graphQLFetcher(graphQLParams) {
        // Get token from local storage
        var token = localStorage.getItem('token')

        // This example expects a GraphQL server at the path /graphql.
        // Change this to point wherever you host your GraphQL server.
        return fetch('/graphql', {
          method: 'post',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': token ? 'Bearer '+ token : null,
          },
          body: JSON.stringify(graphQLParams),
          credentials: 'include',
        }).then(function (response) {
          return response.text();
        }).then(function (responseBody) {
          try {
            return JSON.parse(responseBody);
          } catch (error) {
            return responseBody;
          }
        });
      }

      // Render <GraphiQL /> into the body.
      // See the README in the top level of this module to learn more about
      // how you can customize GraphiQL by providing different values or
      // additional child elements.
      ReactDOM.render(
        React.createElement(GraphiQL, {
          fetcher: graphQLFetcher,
          query: parameters.query,
          variables: parameters.variables,
          operationName: parameters.operationName,
          onEditQuery: onEditQuery,
          onEditVariables: onEditVariables,
          onEditOperationName: onEditOperationName
        }),
        document.getElementById('graphiql')
      );
    </script>
  </body>
</html>
wincent commented 7 years ago

Thanks for sharing @emjaksa.

gauravmakkar commented 6 years ago

@emjaksa Your code is working fine with query operations, what about subscriptions?...

psamd commented 6 years ago

I use passport-http-bearer to parse token on my express server. It parses token from header as well as access_token from query string.

So I add the access_token in the GraphQL endpoint of GraphiQL. Here is my code.

app.get('/graphiql', graphiqlExpress({ endpointURL: '/graphql?access_token=<token>&' }));

This authenticates all requests made by GraphiQL

AndreyMalykhin commented 6 years ago

I've ended up passing access token through Graphiql's Query Variables pane:

{ "accessToken": "azaza" }

imolorhe commented 6 years ago

@AndreyMalykhin You can use https://altair.sirmuel.design/ also.

greghaygood commented 6 years ago

@AndreyMalykhin +1 to Altair over the GraphiQL client if only for the ability to edit the headers when my tokens change (rather than deleting and re-entering them).

nitrnitr commented 6 years ago

What about

GraphiQL::Rails.config.headers['Authorization'] = -> (context) do
  "your jwt"
end

on the graphiql.rb initializer

khaledosman commented 6 years ago

its really sad that this is still open after 3 years... All the solutions provided in the comments are just workarounds or reference to other tools. It shouldn't be too complicated to implement some form in the UI to add the headers.. Are there any intentions to implement this?

I'm currently using Insomnia which is like Postman for graphQL, but I'd rather just use graphiql.

praveenweb commented 5 years ago

@khaledosman - We have a forked version of GraphiQL which adds a header UI. It comes with a configurable endpoint and headers and is hosted online at https://graphiql-online.com.

The source of the fork is here.

Hope its helpful for some people :)

mkanand33 commented 5 years ago

I have a question about graphene authentication...how can I create different tokens for two different logins using graphene -jwt? I want to return a token for user login and also a token for manual admin login? how can I do both?

yami12376 commented 5 years ago

When there will be feature to add headers for tests by default in GraphiQL ?

johndpope commented 5 years ago

I got here from google search found answer in issue below - in the graphql - http headers at bottom just add

{ "Authorization": "Bearer your-token-goes-here" } https://github.com/howtographql/howtographql/issues/448

UPDATE N.B. - actually this is the wrong answer for graph-i-Ql which seems to be missing this component / I didn't realize. I've left this answer above.

Screen Shot 2019-08-14 at 7 41 37 am

You can use prisma graphql playground - https://github.com/prisma/graphql-playground This repo should probably yield to that code base / or cherry pick the headers code to get this in.

acao commented 5 years ago

You can customize headers as they do here, with a custom fetcher: https://github.com/graphql/graphiql#getting-started

westpole commented 5 years ago

I got here from google search found answer in issue below - in the graphql - http headers at bottom just add

{ "Authorization": "Bearer your-token-goes-here" } howtographql/howtographql#448

@johndpope GraphiQL has only "Query variables" section. It does not have "http headers". How did you get that section? What type of UI do you use to test GraphQL queries?

christophermlne commented 5 years ago

@westpole https://github.com/absinthe-graphql/absinthe_plug/blob/master/lib/absinthe/plug/graphiql.ex

Make sure you have configured the correct interface. If you have the correct interface, there is a button you can push to access the headers functionality

sorenhoyer commented 5 years ago

I don't get why on earth we cannot have a "headers" section just like the "query variables" section. Seems awkward, now 4 years later...

benjie commented 5 years ago

@sorenhoyer The reason is that GraphQL is not a HTTP API, it's a function call. It can be consumed locally in your programming language of choice, or over a custom TCP/UDP protocol, or via HTTP, or via Websockets, or through IPC, or through any number of other transports. Headers are only used for the HTTP transport (and, possibly, for websockets, although typically they're not used there by clients). So headers are a concern outside of GraphiQL, only the fetcher function needs to know about them, and that function also lives outside of GraphiQL. You can already add your own interface for managing headers outside of the main GraphiQL component, and you can even add a button inside of GraphiQL to open said interface - this is what I do in PostGraphile's --enhance-graphiql mode in case you want to see an example.

In GraphiQL v1 we're planning to add a plugin system where you can easily insert these "external concerns" into the GraphiQL interface, and a preset system with sharable plugin configurations for a particular use case. We're hoping this will enable people to use a HTTP preset that will pull in common HTTP concerns such as headers, URL bar, cookie configuration, etc. so you can get the interface you want with minimal effort. We've only recently started on this project so it's still a while away (most of us are independent open source developers doing this in our spare time, and only became maintainers very recently) but progress is good.

sorenhoyer commented 5 years ago

Thanks for your elaborate answer @benjie and good luck on your future work on graphiql.

For now, for those of us in need of a graphiql-ish interface, with headers support, I can recommend: https://chrome.google.com/webstore/detail/graphiql-feen/mcbfdonlkfpbfdpimkjilhdneikhfklp

SephReed commented 5 years ago

I'm really confused. In what way is graphiql useful without being able to access authorized data? Is there something I'm missing here? I can't think of any use cases in which I wouldn't eventually need to pass an auth token.

acao commented 5 years ago

The custom fetcher allows you to prescribe any http request pattern you want, you just need to provide the prop to the component.

acao commented 5 years ago

@SephReed the graphiql readme has several examples if you look, you just have to follow the link from the readme

SephReed commented 5 years ago

I've implemented a custom fetcher, and am injecting custom ui. I had been using https://github.com/graphql/express-graphql, but it's "auto graphiql" feature is useless as vanilla graphiql. So now I'm having to reimplement a bunch of stuff.

I don't think graphiql is very useful alone. It seems you kind of have to hack it before it does what it really, really seems like it already should.

benjie commented 5 years ago

That’s not hacking it, that’s exactly how it is intended to be used.

GraphiQL is the building blocks with which you can make a custom GraphQL IDE, and has a very well defined boundary: you worry about the transport and all the transport concerns (via the fetcher function) and GraphiQL worries about the text editing and GraphQL features/documentation.

SephReed commented 5 years ago

that’s exactly how it is intended to be used.

It's awful UX. I'm having to parse through this issue to get the gist, so I can look into the docs and figure out how to reimplement from scratch something which I already had running through another means, all for the sake of adding basic functionality. I get the design principle you're operating by, modularity is important. But everything in moderation.

This is what I would do:

props { 
  showHeaderInputComponent: boolean; // defaults to false
} 

Simple, backwards compatible, easy to understand, doesn't ask the world of the user. There is no way this wouldn't be a better UX than what exists right now.

vinny42 commented 5 years ago

"That’s not hacking it, that’s exactly how it is intended to be used."

That sounds like a very very poor excuse not to have to do work :-) In the end; the majority of applications for graphiql require that it sends headers. The fact that it cannot do so without the user changing code is ridiculous.

This issue has been open for four years, you could save us all a ton of misery by just implementing a basic version of this feature and let whoever needs to override it do so.

I'm sorry, it's just ridiculous.

imolorhe commented 5 years ago

@SephReed @vinny42 The core of the GraphiQL tool isn't specific to HTTP as a transport mechanism (that's what @benjie is saying). While it is true that the majority of the cases so far have used HTTP as a transport mechanism, that is solely as a result of the use cases that exist.

On the other hand, the GraphiQL team was recently restructured and already have this implementation in mind, to make GraphiQL pluggable (https://github.com/graphql/graphiql/issues/829) such that there would be plugins providing some of these features, and also presets that bundle plugins together as they make sense for certain scenarios.

The suggestions here are for working with headers in the current GraphiQL version, and how that can be achieved. Most likely there wouldn't be any modifications to this current (soon to be old) version, and if you don't like the approaches suggested, you could consider other tools for now (there's a number of them available) until the GraphiQL v1.0.0 is released.

Final note: the members of the team maintaining GraphiQL are opensource contributors!

SephReed commented 5 years ago

the members of the team maintaining GraphiQL are opensource contributors

Who runs the internal politics though? I highly doubt that everyone collectively agreed "making http easy is bad." God speed to open-source contributors, but using them as a human shield for what is almost certainly a top down decision is kind of cheap, eh?

Neitsch commented 5 years ago

@SephReed - would you mind laying out how we can improve and make concrete suggestions? Above you made a suggestion about using a different props structure, but I am getting a feeling that there is something overarching missing for you to ramp up on GraphiQL quickly. Open source is great, because people with different ideas come together. Therefore I value your input. I'd be more than happy to review/approve any pull requests that make it even easier to use or improve the documentation :)

SephReed commented 5 years ago

Gladly.

I think it's important to have configuration and I also think it's also important to have user friendly defaults. In terms of pretty much any javascript implementation of graphiql, it seem safe to assume that a good default for fetcher is just fetch pointed at "/graphql." This definitely wouldn't cover every use case, but it's a good starting place.

Following is about all the code I think it should take to setup graphiql, with fetch, pointed to an arbitrary endpoint, with a header component added to the mix:

GraphiQL.renderIn("graphiql", {
  fetcher: "/gql-endpoint",
  showHeaderInputComponent: true,
});

Having 20-50 lines of boilerplate to achieve a simple api with jwt authentication is just a oppressive nuisance when it so easily could be supplied by a tiny function like the one above. This would cover most api use cases... all the ones I can think of at least, but definitely not all. Still, by default, this seems like all it should really take to get a project with graphiql up and running.

SephReed commented 5 years ago

I guess what I'm really trying to say is, you've made a wonderful project. It's very nice and I don't mean to be a bother.

Haseeb717 commented 5 years ago

Is there any way to send header from Graphiql now? which something official from graphiql

khaledosman commented 5 years ago

@SephReed - would you mind laying out how we can improve and make concrete suggestions? Above you made a suggestion about using a different props structure, but I am getting a feeling that there is something overarching missing for you to ramp up on GraphiQL quickly. Open source is great, because people with different ideas come together. Therefore I value your input. I'd be more than happy to review/approve any pull requests that make it even easier to use or improve the documentation :)

With no way to set authentication headers the library is unusable for most people, most people don't build demo todo apps, but real world apps for customers and corporates, so authentication is kinda important.

With no workarounds or recommended way to do it in a README or a documentation file, you're pushing people away from using your open-source tools because you don't even acknowledge it as a typical normal usecase that everyone is going to stumble upon, which is why everyone else in this thread is suggesting an alternative library.

My question is, As a contributor to this library, have you ever used this tool before? have you ever needed to build authentication? if yes, how did you do it?

The cost/effort for building this is little to none compared to the benefit, yet you don't see it as something that should be part of the interface, this issue has been around for 4 years now and its only going to get more comments by more frustrated developers.

vinny42 commented 5 years ago

One last FYI:

We've taken the previous advice and ditched graphiql as "unusable for our purpose".

"While it is true that the majority of the cases so far have used HTTP as a transport mechanism, that is solely as a result of the use cases that exist."

Yes, the majority of use cases are HTTP, that's the point; there is no benefit to being network-protocol-agnostic and refusing to add features for HTTP in a tool that is pretty much only used over HTTP is... I'll let you fill in the bits where I might say bad things.

Thanks for the response, I hope GraphiQL will some day be a good alternative again.