Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
sulliwane opened this issue Nov 17, 2015 · 90 comments

Comments

@sulliwane
Copy link

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
Copy link

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

@sulliwane
Copy link
Author

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

@skevy
Copy link
Contributor

skevy commented Nov 17, 2015

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

@sulliwane
Copy link
Author

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

I close this issue as I have the answer now.

@cancan101
Copy link

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

@sulliwane
Copy link
Author

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

@sulliwane sulliwane reopened this Dec 9, 2015
@leebyron
Copy link
Contributor

leebyron commented Feb 3, 2016

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
Copy link

@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
Copy link

@MarkAndrewJohnson @sulliwane @cancan101 @KyleAMathews Guys, it would be interesting to get your opinion on this graphql/express-graphql#101

@mzohaibqc
Copy link

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
Copy link

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
Copy link

jasonmorita commented Jan 9, 2017

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
Copy link

hunt commented Jan 29, 2017

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
Copy link

estaub commented Jun 12, 2017

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
Copy link

iamsaso commented Jul 7, 2017

Easy implementation to get JWT Token support in GraphiQL

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

screenshot 2017-07-07 09 46 20

@PetrSnobelt
Copy link

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

@emjaksa
Copy link

emjaksa commented Sep 3, 2017

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
Copy link
Contributor

wincent commented Sep 8, 2017

Thanks for sharing @emjaksa.

@gauravmakkar
Copy link

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

@acao
Copy link
Member

acao commented Jun 16, 2020

@connorshea excellent point, yes.

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

@jonaskello
Copy link
Contributor

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
Copy link
Member

acao commented Jun 22, 2020

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
Copy link
Member

acao commented Jun 23, 2020

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

@acao
Copy link
Member

acao commented Jun 24, 2020

released #1593 as graphiql@1.03, enjoy!

@connorshea
Copy link
Contributor

👍 @acao I think we can close this now :)

@jonaskello
Copy link
Contributor

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

@acao
Copy link
Member

acao commented Jun 24, 2020

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 acao closed this as completed Jun 24, 2020
@acao
Copy link
Member

acao commented Jun 25, 2020

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

@jcarroll2007
Copy link

jcarroll2007 commented Jul 24, 2020

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
Copy link

penx commented Sep 4, 2020

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
Copy link
Member

acao commented Sep 4, 2020

@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
Copy link

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
Copy link
Member

benjie commented Feb 18, 2021

You could also use the headers editor:

Screenshot_20210218_145143

@craigmit
Copy link

craigmit commented Feb 19, 2021

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
Copy link

njlr commented Sep 7, 2021

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
Copy link

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

@cedricDevWP
Copy link

@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
Copy link

Akifcan commented Dec 22, 2022

graphqlHTTP({
    schema,
    rootValue,
    graphiql: {
      headerEditorEnabled: true
    }
  })

@KenEucker
Copy link

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
Copy link

njlr commented Jan 19, 2023

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
Copy link

koistya commented May 22, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests