graphile / crystal

đź”® Graphile's Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!
https://graphile.org/
Other
12.58k stars 570 forks source link

Where JWT token is read from and how to use it with GraphiQL? #83

Closed meglio closed 6 years ago

meglio commented 8 years ago

In the documentation, it says:

After a JSON Web Token has been verified and decoded, the resulting claims will be serialized to the PostgreSQL database in two ways:

However, it is not clear where the JWT token is read from?

Also, since I'm using database roles to limit access to the database objects, it would also be very useful to use JWT with GraphiQL - this would allow me to easily test whether all permissions are set correctly for my queries for various roles. Any idea how to do it?

calebmer commented 8 years ago

The JWT is read from your Authorization header. So say you had a token xyz. That token would be retrieved from a header that looked like:

Authorization: Bearer xyz

Currently, there isn’t a way to add headers in the GraphiQL interface, I’m sure there are some roundabout ways of doing it though. Watch @eugenehp’s work in the space though. Some relevant issues:


The recommendation on this though is to create your own custom version GraphiQL and use a custom graphQLFetcher function where you can add the network specific bits (such as Authorization headers). If a version of GraphiQL with headers isn’t available anytime soon, PostGraphQL could do this. For now, see here for a reference implementation of GraphiQL.

meglio commented 8 years ago

Thanks for the very detailed explanation. Okay, for now with GraphiQL I just can use a browser extension to inject an Authorization header when requesting a particular URL.

To store and send JWT from the client: example for Relay, example for Redux. Any other useful links with good examples?

eugenehp commented 8 years ago

@calebmer made a great point.

@meglio I would check the context of the graphql server where you pass headers, and then match them to whatever you have in your authorisation layer JWT, tokens, passport, etc.

meglio commented 8 years ago

Sorry @eugenehp I can't understand what you mean.

eugenehp commented 8 years ago

@meglio It depends on your goal really. From what I understood in your first message, you are looking for a way to check JWT on the side of GraphQL. For that purpose you could utilise GraphQL context and bypass it to your resolve method in the GraphQL type. Let me know if that makes sense.

meglio commented 8 years ago

Sorry still confused. "On the side of GraphQL" - you mean the GraphQL Server? Yes, it seems that postgraphql does it already and runs the "SET LOCAL ROLE" upon successful authentication.

What I was trying to understand is the best practice to manipulate this JWT on the client which works with a GraphQL server.

eugenehp commented 8 years ago

@meglio well, there are few ways to solve it, either pass it as your InputType to your mutation, or do HTTP headers, or do something exotic like doing HTTP query param in your injection layer of something like Relay.

So pretty much all old ways of handling JWT.

Let me know if you have any additional questions.

calebmer commented 8 years ago

I will just say it may be easier than adding a browser extension. Just create your own GraphiQL endpoint and add the header here: https://github.com/graphql/express-graphql/blob/681b627c1a4bf01ea193ee9fe0077dd8d731bd76/src/renderGraphiQL.js#L106-L124, like so:

headers: {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'Authorization': `Bearer ${myToken}`,
}

Where myToken comes from local storage, cookies, a custom UI which prompts you for a token, wherever you’ve chosen to have the user input the token.

You could always use the handy prompt browser function to like in this Codepen: http://codepen.io/calebmer/pen/PzOpjz. Then your code becomes:

headers: {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'Authorization': `Bearer ${prompt('Enter your token:', '')}`,
}

Not the prettiest solution, but it works.

eugenehp commented 8 years ago

I think GraphiQL Mac app does all of that already. So if you are on Mac and want use a standalone app, that might be a good choice too.

On Tuesday, July 12, 2016, Caleb Meredith notifications@github.com wrote:

Closed #83 https://github.com/calebmer/postgraphql/issues/83.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/calebmer/postgraphql/issues/83#event-720142832, or mute the thread https://github.com/notifications/unsubscribe/ABxW78SgBIkKO0G5PCzLbK0Yc4F49wl0ks5qU5kHgaJpZM4JJEGJ .

meglio commented 8 years ago

Just create your own GraphiQL endpoint

It involves making a new node.js project, compiling it and deploying, does it?

calebmer commented 8 years ago

If you plan on deploying it. Or just using PostGraphQL as a library and overriding the GraphiQL endpoint (only exposed when making Accept: text/html requests): https://github.com/calebmer/postgraphql/blob/master/docs/library.md

Although, if the GraphiQL Mac app does that for you like @eugenehp mentioned, that may be your best bet đź‘Ť

chadfurman commented 7 years ago

I would suggest this ticket is not closed until this functionality is built into the project. @calebmer it seems that if the project offers JWT support and encourages row-level security, then the bundled implementation of GraphiQL should also support this functionality.

chadfurman commented 7 years ago

However, installing this extension:

https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj/related

Allows you to set a header:

Authorization: Bearer YOUR_JWT_HERE

calebmer commented 7 years ago

I agree, @chadfurman, I’ll reopen. This functionality was actually implemented by @ferdinandsalis, but I haven’t had the time to go in and merge it yet.

vitalcode commented 7 years ago

See 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.

chris-guidry commented 7 years ago

The Apollo Client Developer Tools Chrome extension has GraphiQL built in and works with JWT authentication. However, this option would only work if you're using Apollo.

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

bradleyayers commented 7 years ago

Another option is using https://github.com/skevy/graphiql-app as it has support for customising HTTP headers:

image

I agree an out-of-the-box solution would be nice here, but really IMO it seems like something that should live in GraphiQL itself, to avoid exactly the situation we have now where there's a handful for bespoke implementations of the same concept.

benjie commented 7 years ago

I know Caleb did some tweaks to the GraphiQL version that we were using (which I may have just broken by upgrading it... Definitely need to check that before release) to make it work better with schema watching - worth keeping in mind.

benjie commented 7 years ago

@bradleyayers Perhaps GraphiQL would accept a PR adding Header/JWT functionality - are there any issues on their tracker talking about it?

bradleyayers commented 7 years ago

There's been a big of discussion about it, and basically the position of GraphiQL is that the network layer is out of scope by design. https://github.com/graphql/graphiql/issues/59#issuecomment-179137697

But it's quite a polarising issue. If GraphiQL wants to stay network agnostic, then presumably something like a graphiql-http project should exist to provide a HTTP-opinionated flavour that includes headers.

eugenehp commented 7 years ago

Bradley,

But how about putting an architecture where we could use middleware and still have the flexibility?

Think like node.js express did with its connect library and later with almost every middleware package.

On Sat, Jul 8, 2017 at 3:12 PM Bradley Ayers notifications@github.com wrote:

There's been a big of discussion about it, and basically the position of GraphiQL is that the network layer is out of scope by design. graphql/graphiql#59 (comment) https://github.com/graphql/graphiql/issues/59#issuecomment-179137697

But it's quite a polarising issue. If GraphiQL wants to stay network agnostic, then presumably something like a graphiql-http project should exist to provide a HTTP-opinionated flavour that includes headers.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/postgraphql/postgraphql/issues/83#issuecomment-313884018, or mute the thread https://github.com/notifications/unsubscribe-auth/ABxW7wLLpWY5soMoSe8ftEDydCH4IWRsks5sL_6_gaJpZM4JJEGJ .

benjie commented 7 years ago

@bradleyayers Thanks for the lo-down, that makes sense. Looking at the GraphiQL docs it seems you can pass it various React children, e.g. GraphiQL.Toolbar, which we could leverage to add this functionality. Are you familiar with React - if so, I think adding this functionality should be fairly straight forward?

bradleyayers commented 7 years ago

Indeed, that sounds like a reasonable approach.

Siyfion commented 7 years ago

Really interested to see this feature added; will save a huge amount of explaining when giving GraphiQL links to "uneducated" sources; "just type in the JWT in headers" vs "Oh yeah, you need to download some specific version, then set headers, point it at the right endpoint...etc"

benjie commented 7 years ago

I'd also like to see this feature added đź‘Ť

parris commented 7 years ago

We've been using Graphql IDE, which is basically a more powerful version of graphiql, and it lets you add headers to every request. Might also be safer than exposing graphiql publicly.

benjie commented 7 years ago

Are you aware of anything that makes GraphiQL inherently unsafe to expose publicly or is it just a case of requiring more effort/knowledge to attack (run your own GraphiQL) and/or increased attack surface area?

chadfurman commented 7 years ago

GraphiQL exposes the full API with complete comments etc. Being able to hide all the comments and documentation on the API in production is important.

Having GraphiQL enabled in production is simply bad form.

On a dev machine, i.e. locally, I'd not be concerned about it myself.

benjie commented 7 years ago

I think there's might be a misunderstanding on what GraphiQL does - all the information you mention is exposed by GraphQL itself directly, GraphiQL simply runs a GraphQL introspection query so it knows what to display. Turning GraphiQL off does not prevent this introspection query from running - you just need to run your own local copy of GraphiQL to execute it.

Here's the GraphQL introspection query you can use to see all the data your GraphQL API exposes:

https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js

For many GraphQL APIs being able to see the documentation is important (take, for example, the GitHub GraphQL API), so I don't agree that having GraphiQL in production is bad form. https://developer.github.com/v4/explorer/

If you wish to lock down what end-users can see I'd advise that you filter the queries that are able to execute against your API. (Actually I'd advise that you always do this anyway!) There are a few ways to do this depending on what you want to achieve. The best, in my opinion, is to use persisted queries which acts as a query whitelist - that way only very specific GraphQL queries are allowed. This isn't always appropriate though, so another would be to perform some additional validation on the GraphQL queries that are received by the server. You might like to look at a few of the GraphQL built-in validations in this case:

https://github.com/graphql/graphql-js/blob/master/src/validation/index.js

Of course in v4 you could also add a fairly simple plugin that would remove the comment from every single field exposed by GraphQL, if those comments are something you don't want exposed to end users.

Hope this helps!

chadfurman commented 7 years ago

:+1: removing comment fields with a plugin sounds like a great plan.

Yes, there are cases where you want to expose your API documentation. Facebook etc have a public API designed to be used by third-parties. This is not always the case, and I would hazard a guess that in the majority of cases this is not true.

Could you elaborate on persisted queries? You've mentioned them a couple of times and I'm not sure how to implement them with Postgraphql / Relay Modern.

benjie commented 7 years ago

I've hacked together support for a client's app using my forks:

https://github.com/benjie/persistgraphql-webpack-plugin https://github.com/benjie/persistgraphql

Which instead of numbering the queries instead hashes them.

You can then mod your network layer to send instead of the query the query hash; then on the server you can prefix the graphql endpoint with a middleware which overwrites the query with the persisted query by looking up the hash received. It's purely a network layer concern this way. The hashed queries can be stored anywhere; personally I track them in git but they could be uploaded to S3 or similar instead.

I've not had time to publish anything on this yet.

chadfurman commented 7 years ago

@benjie makes sense. Modding the network layer to transmit / receive query hashes dynamically is an interesting approach

benjie commented 6 years ago

[semi-automated message] Hi, there has been no activity in this issue for over 8 weeks so I'm closing it to keep the issues/pull request manageable. If this is still an issue, please re-open with a comment explaining why.

prabhupant commented 4 years ago

Use postman. The latest version of postman supports GraphQL

benjie commented 4 years ago

You can use PostGraphile's --enhance-graphiql to gain a (simple) header editor.