graphql

bartuka 2019-05-14T10:55:33.100900Z

hi everyone, I'm using lacinia-pedestal and I'm with trouble to implement authorization and authentication on my project. You guys have any example of how to do that using lacinia-pedestal so I can study a bit more?

orestis 2019-05-14T11:13:33.102100Z

I can give some high-level pointers but it’s hard giving details without sharing my entire codebase, which I can’t do 😞

orestis 2019-05-14T11:14:45.103600Z

Usually you would create a pedestal interceptor that deals with authentication, that is, given an HTTP request see if you can somehow lookup a user. You’d do this based on headers, cookies and looking something up in some kind of a DB, whatever you are using.

orestis 2019-05-14T11:15:30.104700Z

Once you have a user, then another pedestal interceptor can decide whether the user should actually have access to the graphql endpoint (perhaps graphql is only available to admins)

orestis 2019-05-14T11:16:03.105400Z

Then finally, inside every lacinia resolver you can access the context and figure out if the user should have access to that resolver.

orestis 2019-05-14T11:16:37.106300Z

That’s the high-level overview. There are some cool thing you could do to make the last step a little bit more automatic and/or data driven.

bartuka 2019-05-14T11:19:22.108500Z

thanks for the overview! I was looking at the pedestal documentation to see how to create interceptors like those you described (https://github.com/pedestal/pedestal/blob/master/samples/buddy-auth/src/buddy_auth/service.clj)

bartuka 2019-05-14T11:20:11.109300Z

but in the end of the day, I imagined that I needed to code the authorizaton part on the resolver level

bartuka 2019-05-14T11:21:00.110100Z

I think this should not be a problem, maybe a simple function to verify permissions would do it

orestis 2019-05-14T11:30:51.111700Z

The way we’re doing is that essentially the first step on the resolver is validating permissions using a boolean function. We are thinking about perhaps writing a macro to automate this a little bit or attaching some permissions in the .edn file that we use to create the schema, so that it’s more declarative.

bartuka 2019-05-14T11:32:32.113100Z

cool. I got it. In order to add an interceptor to lacinia-pedestal I just need to write the interceptor and add it on (lacinia.pedestal/service-map {:graph-ql true :interceptors my-custom-interceptor}) ?

orestis 2019-05-14T11:37:31.114200Z

(defn prod-routes
  "Return the prod routes for lacinia. Accepts an optional interceptors-fn that
  can change the interceptors sequence for all graphql routes."
  ([] (prod-routes identity))
  ([interceptors-fn]
   (let [schema (my.app.schema/load-schema)]
     (lacinia/graphql-routes schema {:graphiql false
                                     :interceptors (-> (lacinia/default-interceptors schema {})
                                                       (replace-interceptor ::lacinia/json-response json-response-interceptor)
                                                       interceptors-fn)
                                     :get-enabled false}))))

orestis 2019-05-14T11:38:40.115200Z

This does two things — first it replaces the interceptor responsible to convert to json with a custom one, which I had to tweak. The second is it accepts a function that takes a vector of interceptors and returns another vector of interceptors.

orestis 2019-05-14T11:38:52.115600Z

This way you can prepend your auth interceptors

bartuka 2019-05-14T11:39:03.115800Z

greaat!! thankx!

orestis 2019-05-14T11:39:48.116200Z

(def ^{:doc "Interceptors that ensure an API endpoint is auth-only."}
  api-interceptors
  [ring-middlewares/cookies
   my.app.interceptors/attach-site
   my.app.interceptors/authentication
   my.app.interceptors/authenticated-api])

(defn lacinia-prod-interceptors
  "Massage the default lacinia interceptor seq to be auth-only."
  [interceptors]
  (concat api-interceptors interceptors))

(defn lacinia-dev-interceptors
  [interceptors]
  (concat
   [my.app.interceptors/dev-api-interceptor
    my.app.interceptors/authenticated-api]
   interceptors))

orestis 2019-05-14T11:40:21.116700Z

The cool thing with this is that I can reuse api-interceptors for other non-lacinia endpoints.

orestis 2019-05-14T11:40:54.117300Z

dev-api-interceptor is something that adds a fake user to the request so that GraphiQL works out of the box.

bartuka 2019-05-14T11:41:27.117500Z

nice!

lilactown 2019-05-14T14:35:45.118200Z

I just discovered https://github.com/workframers/artemis

lilactown 2019-05-14T14:35:56.118600Z

reading the docs now, but it seems really nice!

lilactown 2019-05-14T14:37:09.119Z

ah, except it uses core.async 😞 hopefully there’s a way to just get a promise-based value as well

timgilbert 2019-05-14T17:38:30.120Z

Hey, my company released artemis (though I'm not personally as familiar with it as some other folks here). Hit me up if you have questions

timgilbert 2019-05-14T17:39:05.120500Z

Wait, is core.async not the new hotness any more?

lilactown 2019-05-14T17:42:06.121500Z

I prefer to keep core.async out of my projects. I don’t really find it useful for most front-end development, and would rather it returned a promise that I could choose how to plumb into my app (core.async, promesa, what-have-you)

lilactown 2019-05-14T17:43:05.122300Z

especially since React will sometime this year have first-class support for promise-based data fetching via React Suspense

timgilbert 2019-05-14T17:47:01.123200Z

I see. I don't know the guts of artemis super-well, but it seems like it would be possible to have it deal with promises instead

lilactown 2019-05-14T17:50:02.124200Z

yeah maybe I’ll open an issue

👍 1
pcj 2019-05-14T17:51:44.125600Z

Is it considered bad practice to nest executes? I want to return the object after a create mutation

hlship 2019-05-14T18:27:40.127200Z

Some of the lacinia-pedestal code has evolved a little bit unevenly. At the core, though, is the ability to generate a interceptor pipeline that services GraphQL and GraphiQL requests, but there's an inject function that is useful for adding new interceptors into the pipeline; this helps insulate your code from minor changes to the pipeline that may be added in later releases of lacinia-pedestal.

👍 1
hlship 2019-05-14T20:03:46.127600Z

Order of execution can be an issue; the order in which keys are returned in the response is based on the order of the fields in the query BUT that may not exactly match the order of execution, if that makes a difference. There's a number of factors involved.

hlship 2019-05-14T20:04:13.127800Z

Whereas top-level mutations in a mutation request execute in a specific order, as per the spec.

hlship 2019-05-14T20:05:10.128Z

Currently, there isn't a way for fields to share context sideways (to peers), only downward (to sub-fields). You could address this by having a in interceptor create and store a shared atom into the field resolver context.

pcj 2019-05-14T20:26:19.128300Z

Thank you for this explanation! I'm going to try and see if this works the way I'm thinking it will. If not I'll just call the same function I'm using for the resolver and manually add the fields I want to include in the response