Fork me on GitHub
#graphql
<
2019-05-14
>
bartuka10:05:33

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?

orestis11:05:33

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

orestis11:05:45

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.

orestis11:05:30

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)

orestis11:05:03

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

orestis11:05:37

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.

bartuka11:05:22

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)

bartuka11:05:11

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

bartuka11:05:00

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

orestis11:05:51

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.

bartuka11:05:32

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}) ?

orestis11:05:31

(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}))))

orestis11:05:40

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.

orestis11:05:52

This way you can prepend your auth interceptors

bartuka11:05:03

greaat!! thankx!

orestis11:05:48

(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))

orestis11:05:21

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

orestis11:05:54

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

lilactown14:05:56

reading the docs now, but it seems really nice!

lilactown14:05:09

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

rakowa04:05:12

Curious as to why core.async might be a disadvantage.

lilactown04:05:52

I answered below this comment. basically, it is less general

rakowa05:05:49

sorry, shoulda looked down!

timgilbert17:05:30

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

timgilbert17:05:05

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

lilactown17:05:06

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)

lilactown17:05:05

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

timgilbert17:05:01

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

lilactown17:05:02

yeah maybe I’ll open an issue

👍 1
pcj17:05:44

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

hlship20:05:46

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.

hlship20:05:13

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

hlship20:05:10

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.

pcj20:05:19

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

hlship18:05:40

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