yada

stijn 2019-01-21T13:08:48.235300Z

@kwladyka https://gist.github.com/stijnopheide/af90400b26a686e1145b9a5e2f0a2874 should get you started with lacinia + yada + authentication

kwladyka 2019-01-21T13:09:22.235800Z

Thank you, I found good article about auth for GraphQL https://www.apollographql.com/docs/apollo-server/features/authentication.html

kwladyka 2019-01-21T13:09:55.236800Z

How do you solve download files?

stijn 2019-01-21T13:09:59.237Z

just to be sure, that's on older versions of yada / lacinia, but I think they are compatible with the current release

kwladyka 2019-01-21T13:10:03.237300Z

Sessions?

stijn 2019-01-21T13:10:38.238300Z

the only file upload / download we have in this app is images and we have separate resources for that (outside of graphql)

stijn 2019-01-21T13:10:53.238800Z

and yes we use the same cookie as we use for the authentication with the graphql endpoint

kwladyka 2019-01-21T13:11:43.240500Z

In other words I don’t see option to do auth only in GraphQL, because there is no way to not use REST, because there is always needs for download files

kwladyka 2019-01-21T13:11:45.240700Z

and upload

stijn 2019-01-21T13:11:51.240900Z

the example gist includes a bit more of the machinery of graphql query parsing than the edge example (https://github.com/juxt/edge/blob/master/phonebook-graphql/src/edge/phonebook/graphql.clj) because according to the spec you can also put your query in a GET request and choose whether you put it in the body or query parameter

stijn 2019-01-21T13:12:14.241600Z

it matched (at some point) the implementation from lacinia-pedestal, but I didn't bother keeping it up to date

stijn 2019-01-21T13:12:39.242100Z

yeah, I don't think that auth in GraphQL is the best option, UNLESS you only use that one endpoint

stijn 2019-01-21T13:13:17.242900Z

the advantage of relying on auth outside of graphql is that you can e.g. use browser cookies

kwladyka 2019-01-21T13:13:46.243500Z

Do you know how to use Redis with yada for sessions?

kwladyka 2019-01-21T13:13:57.243900Z

Is any middleware like for ring for that purpose?

kwladyka 2019-01-21T13:14:08.244200Z

Can I use ring middleware with yada?

kwladyka 2019-01-21T13:14:40.244400Z

https://github.com/clojusc/ring-redis-session

stijn 2019-01-21T13:15:59.245800Z

you could do ring middleware with yada, but you have to translate it into interceptors (i.e. the pre-processing and post-processing need to be split because yada is asynchronous)

borkdude 2019-01-21T13:16:00.245900Z

@kwladyka we save sessions in an atom, but we could easily swap it out with Redis. It’s something we do in our auth scheme.

stijn 2019-01-21T13:16:31.246400Z

and we do sessions in our datomic database, where we stick it next to the user

kwladyka 2019-01-21T13:17:03.247Z

sure, just I would like to glue Redis with yada, instead make +100 lines of code with my own solution

kwladyka 2019-01-21T13:17:23.247400Z

with ring it is 1 line 😉

borkdude 2019-01-21T13:17:37.247900Z

(defonce user-profiles
  (atom {}))

(defmethod yada.security/verify "...." ...) 

kwladyka 2019-01-21T13:17:42.248100Z

There is no info in doc how to do interceptors in yada

kwladyka 2019-01-21T13:18:13.248500Z

*doc in progress

kwladyka 2019-01-21T13:18:32.249Z

oh it could be quite easy hmm

borkdude 2019-01-21T13:18:40.249200Z

you should read the part about security, because that’s the place where you should probably hook into (although you could do it with your own interceptor, just more work): https://juxt.pro/yada/manual/index.html#security

kwladyka 2019-01-21T13:20:14.250400Z

thanks, I have to learn how to manage sessions in yada before make this auth

borkdude 2019-01-21T13:20:56.250700Z

yada even has support for roles and authorization, which we also use

stijn 2019-01-21T13:21:35.251100Z

here's cookie authentication for yada (read side): https://gist.github.com/stijnopheide/af90400b26a686e1145b9a5e2f0a2874#file-api-clj-L1

stijn 2019-01-21T13:21:48.251300Z

it's not going to take 100 lines

kwladyka 2019-01-21T13:22:25.252Z

thanks, I have to read all this things now 😉

stijn 2019-01-21T13:22:45.252400Z

cookies are parsed by yada and put in the context, you have to implement a verify function for your auth and check the db if the cookie is valid

stijn 2019-01-21T13:23:15.253200Z

and like @borkdude suggests, start off with an atom and swap that out with a database once you have the need to have more than 1 instance of your webserver running

stijn 2019-01-21T13:23:46.253900Z

(or configure your load balancer to have sticky sessions :-D)

borkdude 2019-01-21T13:25:19.255200Z

we have to deal with SSO and cookies from another subdomain. it wasn’t that hard to make it work

kwladyka 2019-01-21T13:25:24.255300Z

yes it is good idea. On the other hand I assume I will do often update on the beginning and people will lost they session everyday 😉

borkdude 2019-01-21T13:25:47.255700Z

we verify our session every hour against some third party SSO service

malcolmsparks 2019-01-21T13:45:15.258400Z

@stijn is right, but things are improving in 1.3.0. In 1.2.x the cookies were parsed by the swagger parameters code so it was a useful side-effect they were available for response functions to use. 1.3.x formalises cookies - see https://github.com/juxt/yada/blob/master/doc/cookies.adoc - they are now declarative (because it's useful to have cookie parameters as data for further introspection, rather than having to hand-code them - of course you can still hand-code the traditional way if you need that).

malcolmsparks 2019-01-21T13:46:51.260300Z

cookie declarations can also specify a :consumer function - that means you can register a callback which will get called, and only called, with the value of the incoming cookie. Yada will work out which cookies belong to which consumers. Your cookie consumer gets the yada context, and can return it augmented with anything you like, including credentials which you can use later on in your authorization functions.

malcolmsparks 2019-01-21T13:47:53.261300Z

in future, it might be possible to specify a signing or encryption key

malcolmsparks 2019-01-21T13:48:00.261600Z

RFC 6265 Section 8.3:

malcolmsparks 2019-01-21T13:48:02.261800Z

> Servers SHOULD encrypt and sign the contents of cookies (using whatever format the server desires) when transmitting them to the user agent (even when sending the cookies over a secure channel).

malcolmsparks 2019-01-21T13:48:22.262200Z

yada will be able to do that for you (signing, encrypting, and checking incoming) once the declarations are in place

👍 3
malcolmsparks 2019-01-21T13:48:54.262900Z

I've tried very carefully to preserve compatibility with previous yada versions. If, for any reason, your existing code doesn't work with 1.3.x, let me know because that's a bug.

borkdude 2019-01-21T13:49:24.263500Z

alright, I’ll try out 1.3.0 and see if our scheme still works

borkdude 2019-01-21T13:50:08.264500Z

we haven’t upgraded in a while:

;; Yada
     [yada "1.2.15"] ;; includes Ring 1.6.0-Beta
     [aleph "0.4.6"]

malcolmsparks 2019-01-21T13:51:09.265600Z

great - thanks @borkdude - I'll keep the alpha label for a while until things stabilizes. 1.2.15 is the most recent 1.2.x, there hasn't been many releases recently. I've pulled 1.2.16 due to a compatibility issue

borkdude 2019-01-21T13:51:49.266500Z

pulled back from clojars?

malcolmsparks 2019-01-21T13:52:02.266900Z

@kwladyka I've been thinking about your feedback about the manual. I think it would be good to create a cheatsheet explaining all the keys you can put in a yada resource, for quick reference.

kwladyka 2019-01-21T13:52:26.267200Z

oh yes, it will be very good

kwladyka 2019-01-21T13:53:29.268200Z

there will be great to add to tutorial example of how to write yada POST (form / body )+ maybe GraphQL + how to write tests for that

kwladyka 2019-01-21T13:53:41.268500Z

I took me about 1 week to figure out

kwladyka 2019-01-21T13:54:08.268900Z

Most of people can give up 🙂

kwladyka 2019-01-21T13:55:18.270500Z

*but probably I lost most of the time for my magic bug 🙂

borkdude 2019-01-21T13:55:24.270900Z

@malcolmsparks so far it still seems to work. I’ll keep you posted. I’m still using aleph 0.4.6.

malcolmsparks 2019-01-21T13:55:30.271100Z

Edge has a phonebook example for form/body posts and some GraphQL integration. yada does integrate with lacinia, including subscriptions, very well

malcolmsparks 2019-01-21T13:55:58.271900Z

@borkdude me too - I can't get more recent versions of aleph to work - I'm getting a weird stack trace whenever I try

kwladyka 2019-01-21T13:56:30.272500Z

Yes there is an example, but without tests

malcolmsparks 2019-01-21T13:57:15.273900Z

@kwladyka I've fixed an issue this morning that caused tests to fail when upgrading from ring 1.6.0 to ring 1.7.1 - that might have been your problem

kwladyka 2019-01-21T13:57:23.274100Z

At that moment I do tests like that:

(defmacro with-server [handler build-url & body]
  `(let [listener# (yada/listener ~handler)
         close# (:close listener#)
         port# (:port listener#)
         ~build-url (fn [path#]
                      (str "<http://localhost:>" port# path#))]
     (try
       ~@body
       (finally
         (close#)))))

(deftest graphql-test
  (with-server core/handler build-url
    (testing "Graphql"
      (is (= 405 (-&gt; @(http/request
                        {:url (build-url "/graphql")
                         :method :get})
                     :status))
          "GET is not allowed.")
      (is (= 401 (-&gt; @(http/request
                        {:url (build-url "/graphql")
                         :headers {"Content-Type" "application/json"}
                         :method :post
                         :body "{\"email\":\"<mailto:foo@example.com|foo@example.com>\",\"password\":\"qwaszx\"}"})
                     :status))
          "Not authorized")
      #_(is (= 200 (-&gt; @(http/request
                          {:url (build-url "/graphql")
                           :headers {"Content-Type" "application/json"}
                           :method :post
                           :params {:query "{ game_by_id(id: \"1237\") { name designers { name }}}"}})
                       :status))
            "Authentication by session")
      (is (= 200 (-&gt; @(http/request
                        {:url (build-url "/graphql")
                         :headers {"Content-Type" "application/json"
                                   "Authorization" "bearer 89abddfb-2cff-4fda-83e6-13221f0c3d4f"}
                         :method :post
                         :body "{\"email\":\"<mailto:foo@example.com|foo@example.com>\",\"password\":\"qwaszx\"}"})
                     :status))
          "Authentication by token"))))

stijn 2019-01-21T13:57:40.274500Z

@malcolmsparks I will try the alpha too. Any other changes that we need to be aware of?

kwladyka 2019-01-21T13:58:15.275800Z

no, the issue is it is not obviously yada/response-for doesn’t work with :body

kwladyka 2019-01-21T13:58:33.276900Z

I mean, there is no way to send JSON body for example

malcolmsparks 2019-01-21T13:58:35.277Z

mostly the changes are confined to a new security design (using new entries in the resource). The old design and old code is still supported, so as long as you don't mix old/new in the same resource, everything should work fine

malcolmsparks 2019-01-21T13:58:52.277700Z

(if you do mix, the schema validation will protect you)

borkdude 2019-01-21T13:59:06.278300Z

@malcolmsparks is it recommended to upgrade to any new things? migration release notes?

malcolmsparks 2019-01-21T13:59:46.279600Z

I don't think there's any migration issues with 1.3.x - it should work fine with existing yada code -

kwladyka 2019-01-21T13:59:57.280100Z

So on the end I have to run listener normally and normally use HTTP client. But it is not obviously on the beginning.

stijn 2019-01-21T14:00:13.280600Z

Bidi question: is there a way to do request logging at the bidi level? my use case is, I want to log ALL requests in a bidi tree, also the ones that generate a 404 because there is no matching route. I know about the catch-all routes and could insert a resource at the end, but 1/ if I understand bidi matching correctly I would need to do it for each subtree? 2/ a swaggered endpoint doesn't like a 'true' route.

malcolmsparks 2019-01-21T14:00:21.280800Z

@kwladyka yeah, there could be more help and support around how to test

kwladyka 2019-01-21T14:00:32.281200Z

Especially when I have habit to use tools like https://github.com/xeqi/peridot

malcolmsparks 2019-01-21T14:01:24.282100Z

@stijn you only need one catch-all at the end of the structure. if a sub-tree 'fails' to match, matching continues

stijn 2019-01-21T14:01:39.282500Z

oh OK, I didn't know that

malcolmsparks 2019-01-21T14:03:01.284100Z

but some way of logging would be nice - a design feature of bidi is that it separates the data structure containing the routing with the algos that does the matching. So it should be possible to provide a slower algorithm that performs logging which could be enabled on a per-request basis.

malcolmsparks 2019-01-21T14:03:20.284400Z

it is sometimes a real pain to debug bidi routes

borkdude 2019-01-21T14:03:42.284900Z

yeah, that’s a pain

borkdude 2019-01-21T14:04:06.285500Z

especially the compact vector notation

kwladyka 2019-01-21T14:04:32.286100Z

There are also more questions like: :consumes #{"application/x-www-form-urlencoded" "application/edn" "application/json"} So how to write :parameters {:body s/Any} On different content type it should be validate different probably. Sometimes it is a String, sometimes EDN, sometimes normal form

kwladyka 2019-01-21T14:05:49.287600Z

so while resource require :body when it is JSON and :form :query when it is application/x-www-form-urlencoded, no idea how to code it at that moment

malcolmsparks 2019-01-21T14:05:57.287900Z

yada will coerce the request body to the right 'shape'. If the content differs on a per-content-type basis, then you need to validate in the response function. The response function can call yada/content-type on the context to discover which type was negotiated

malcolmsparks 2019-01-21T14:07:20.289100Z

@kwladyka I think this is a general problem with how Swagger views the world. In HTTP, there are only request bodies. There is no difference between a form and a body.

kwladyka 2019-01-21T14:07:51.290200Z

Mainly I want to show you issues for person how start with yada to let you make better tutorial 🙂

malcolmsparks 2019-01-21T14:07:58.290300Z

Swagger makes assumptions about how HTTP applications should be written which is somewhat narrower than HTTP.

malcolmsparks 2019-01-21T14:08:05.290600Z

Yes, I appreciate that @kwladyka, thanks

malcolmsparks 2019-01-21T14:10:41.293200Z

The nice thing about IETF RFCs is that the authors make a real effort to keep everything consistent with other RFCs. However, with Swagger, the authors are not so constrained and I feel they complicate things. One of my regrets with yada's design is making parameters a core feature - I'd prefer to deprecate existing parameter declarations and find another way, via extensions, to support Swagger. The Swagger support is so old in yada I'm not sure if new users use it any more. But I've heard good arguments that Swagger should be retained in yada.

malcolmsparks 2019-01-21T14:11:26.294Z

So in future versions of yada I think there'll be alternative approaches to dealing with parameters, form validation, etc. Perhaps using libraries that more naturally fit those domains.

borkdude 2019-01-21T14:11:46.294600Z

We’re actively using the swagger support in yada

malcolmsparks 2019-01-21T14:12:33.295400Z

Good to know. There's obviously a lot that's happened in the Swagger world since the first yada releases, for example, renaming to OpenAPI

malcolmsparks 2019-01-21T14:12:55.296500Z

I know a lot of teams find swagger useful to communicate their APIs with downstream consumers

borkdude 2019-01-21T14:12:56.296600Z

I’m open to upgrading our codebase to something newer. Migration guide would be helpful

borkdude 2019-01-21T14:13:11.297200Z

yes, that’s what we are doing

kwladyka 2019-01-21T14:13:18.297500Z

@malcolmsparks from my point of view about form validator everything can be good, unless it force me to have validation code in 2 different forms. I mean if I will have to have spec and special yada code for doing the same it is bad for me 🙂

malcolmsparks 2019-01-21T14:13:26.297700Z

@borkdude do you use the built-in swagger console? or just the swagger decls that yada resources produce

borkdude 2019-01-21T14:13:54.298300Z

let me check

malcolmsparks 2019-01-21T14:14:21.298800Z

@kwladyka yes, I think it's better to allow users to do their own parameter handling and validation - but provide the callbacks in the right place so that code can indicate failure and error messages for 400 responses.

👍 1
borkdude 2019-01-21T14:15:06.299500Z

I think we’re using the builtin thing:

(defn new-swagger-resource
  [system routes]
  (resource
   system
   (let [spec (yada/swagger-spec
               routes
               {:info {:title "DOC Search API"
                       :version "1.0.0"
                       :description (slurp
                                     (io/resource
                                      "swagger/description.md"))}
                :tags api-tags})]
     {:response (fn [ctx] spec)})))

["/swagger-ui" (-&gt;
                    (yada/new-webjar-resource "swagger-ui" {:index-files ["index.html"]})
                    ;; Tag it so we can create an href to the Swagger UI
                    (tag :dre.resources/swagger))]

malcolmsparks 2019-01-21T14:15:18.300Z

right - thanks

borkdude 2019-01-21T14:15:30.300500Z

this was actually one of the things that made me choose yada over pedestal. it was hard to get swagger working with pedestal I found (2 years ago)

malcolmsparks 2019-01-21T14:15:35.300600Z

that swagger-ui is quite old now, i'm considering a refresh

borkdude 2019-01-21T14:16:07.301600Z

the lib I found for that was very macro-heavy, I decided not to go ahead with it after giving it a try

malcolmsparks 2019-01-21T14:16:24.302100Z

I am not suggesting for a moment that yada will remove swagger, but it may be necessary for you to add an extra library dependency at some point in the future

borkdude 2019-01-21T14:16:27.302300Z

(I believe it was also made by someone from juxt)

malcolmsparks 2019-01-21T14:17:00.303200Z

yes, we've done a lot with pedestal over the years, on various projects. I know what you mean about the macros.

borkdude 2019-01-21T14:17:04.303500Z

no problem. I was referring to an alternative to parameters. I wouldn’t mind upgrading, but a guide would be most helpful

borkdude 2019-01-21T14:17:35.304500Z

this was the lib in question: https://github.com/frankiesardo/route-swagger

stijn 2019-01-21T14:17:41.305Z

we use swagger for a lack of alternative, but I'm not at all a fan

malcolmsparks 2019-01-21T14:18:04.305700Z

The existing parameters design will be kept. Any new designs will require new yada context keys. Things will grow by accretion, I don't feel like going back and rewriting old working code.

borkdude 2019-01-21T14:18:19.306300Z

cool

stijn 2019-01-21T14:18:48.307Z

it feels indeed like yada is 'opinionated' towards json that way. we do XML and edn/transit too and it doesn't really have any support for that (I mean in terms of documentation, not the processing)

kwladyka 2019-01-21T14:20:43.308400Z

I have to go, see you later! 🙂

stijn 2019-01-21T14:21:03.308800Z

but it's hard to support several formats I guess, I wouldn't know how a tool could communicate XML structure / json schema and transit/edn

borkdude 2019-01-21T15:08:44.309700Z

@malcolmsparks I remember upgrading the swagger UI once, but our team didn’t like the change. big responses made the UI unresponsive. I don’t remember the details, but we decided to revert it.

👍 1
kwladyka 2019-01-21T20:32:44.310700Z

Is https://juxt.pro/yada/manual/index.html#cookie-authentication is outdate and doesn’t work?

kwladyka 2019-01-21T20:33:19.311300Z

when use :cookie > Cannot turn resource-model into resource, because it doesn’t conform to a resource-model schema

kwladyka 2019-01-21T20:34:55.311800Z

{:realms {"session" {:authentication-schemes [{:scheme :cookie
                                                                    ;:cookie  "session"
                                                                    :verify (fn [cookie]
                                                                              (println "auth cookie" cookie)
                                                                              {:a 1})}]
                                          :authorization {:validate (fn [ctx creds]
                                                                      (println "creds" creds)
                                                                      ctx)}}}}
authentication is never run

kwladyka 2019-01-21T20:36:14.312300Z

I don’t see in code defmethod verify :cookie. Maybe it is in the doc, but in code?

kwladyka 2019-01-21T20:51:20.312500Z

https://github.com/juxt/yada/pull/248

kwladyka 2019-01-21T20:52:09.313Z

^ok it is confirmed

borkdude 2019-01-21T20:55:29.313600Z

FWIW, I have:

(defmethod yada.security/verify "my_auth_scheme"
  [{:keys [resource cookies] :as ctx}
   {:keys [_verify _scheme]}]
 ...
You can do whatever you like there with the cookie

borkdude 2019-01-21T20:56:35.314200Z

I’m looking up the cookie in the context like this: (get-in ctx [:cookies ".MYCOOKIE"])

kwladyka 2019-01-21T21:04:11.314700Z

I am thinking if I can use this one https://github.com/juxt/yada/blob/master/ext/oauth2/src/yada/oauth.clj#L242 but I am to tired today. It is time to go sleep.

kwladyka 2019-01-21T21:04:19.315Z

Good night

malcolmsparks 2019-01-21T21:52:07.315700Z

@kwladyka cookie auth is out-of-date, a replacement is coming. Basically the replacement is explained here: https://juxt.pro/yada/manual/index.html#cookies

malcolmsparks 2019-01-21T21:52:53.316600Z

The summary is that there is no such thing as 'cookie auth' - there is HTTP auth schemes which use the Authorization header, and there is state management via cookies.

malcolmsparks 2019-01-21T21:53:49.317900Z

The new 1.3.x version of yada allows you to do your own auth with cookies in that cookie consumers can add info the the context for the authorize interceptor to work with.