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

Allowing graphiql to use an access token #202

Closed
benkovy opened this issue Oct 12, 2021 · 2 comments
Closed

Allowing graphiql to use an access token #202

benkovy opened this issue Oct 12, 2021 · 2 comments

Comments

@benkovy
Copy link

benkovy commented Oct 12, 2021

During development the graphiql interface is a huge bonus. But it becomes difficult when you have resources that require authentication.

If you have devise configured to change access tokens on every request this can be even more difficult.

I have a hacky solution that I am using and thought I might share it here.

  1. I removed the graphiql-rails gem from my Gemfile.
  2. I changed my routes to include
if Rails.env.development?
  get "/graphiql", to: "graphql#graphiql"
end
  1. Next I added a file under views -> views/graphql/graphiql.html.erb (see bottom of issue for the example file)

This uses a CDN to pull down react, react-dom and graphiql and defines a custom fetcher that populates the headers that devise looks for when sending a request to a protected resource. It also populates the graphiql interface with a default login mutation (you can add your test account credentials in the snippet below). So all that is needed to get started, is to run the login mutation.

Now I guess there are security concerns if those CDN's ever change or are compromised but this is only ever used locally...

graphiql.html.erb
<html>
<head>
  <title>GraphiQL</title>
  <link href="https://unpkg.com/graphiql/graphiql.min.css" rel="stylesheet" />
</head>
<body style="margin: 0;">
<div id="graphiql" style="height: 100vh;"></div>

<script crossorigin src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://unpkg.com/graphiql/graphiql.min.js"></script>

<script>
  const query = `
mutation login {
  userLogin(email: "[email protected]", password: "password") {
    credentials {
      accessToken
      client
      expiry
      uid
      tokenType
    }
  }
}
  `;

  const graphQLFetcher = async (graphQLParams, opts) => {
    const lsAuth = localStorage.getItem('auth');
    const authHeaders = lsAuth ? JSON.parse(lsAuth) : {};

    const response = await fetch('<%= graphql_path %>', {
      method: 'post',
      headers: { 
        'Content-Type': 'application/json',
        ...authHeaders,
      },
      body: JSON.stringify(graphQLParams),
    });

    const {headers} = response;
    const auth = {
      'access-token': headers.get('access-token'),
      client: headers.get('client'),
      expiry: headers.get('expiry'),
      uid: headers.get('uid')
    };

    if (auth['access-token'] && auth.expiry) {
      localStorage.setItem('auth', JSON.stringify(auth));
    }
    
    try {
      const json = await response.json();
      return json;
    } catch {
      const text = await response.text();
      return text;
    }
  }

  ReactDOM.render(
    React.createElement(GraphiQL, { fetcher: graphQLFetcher, defaultQuery: query}),
    document.getElementById('graphiql'),
  );
</script>
</body>
</html>

inspiration: rmosolgo/graphiql-rails#85 (comment)

@mcelicalderon
Copy link
Member

Hey @benkovy! Thank you for opening this and sorry we've taken so long to reply. I will take a look at this soon 👍

@mcelicalderon
Copy link
Member

Hello again, @benkovy! Sorry we took so long to reply. Thank you for looking into this a coming up with a solution 🚀

Now I guess there are security concerns if those CDN's ever change or are compromised but this is only ever used locally.

That's a real concern, it would be risky for anyone to start using this gem if it pulls code from anywhere, even if most of the time it would be setup only for dev envs. Also, I don't think we are going to include something in this gem related to views. We are trying to keep the API only as much as possible. So I can't think of a complete solution right now, that is similar to what you have proposed.

Now, this might not be ideal, but I can think of something you can do and I have done in the past.

  1. Setup Devise Token Auth's initializer to change tokens on each request unless Rails.env.development?.
  2. Use a browser extension to modify headers. I have used https://modheader.com/install before and it works just fine. This will allow you to set the headers once and make as many requests as you need.
  3. (Optional) You could also set the expiration time for the tokens to a long period of time like 1.month if Rails.env.development?.

I hope that helps! I'm going to close this issue as I don't see a way to incorporate something like this in the gem. But as usual, feel free to keep discussing alternatives in this issue as it might help someone else. Just careful with security 😉

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

2 participants