funcool

A channel for discussing and asking questions about Funcool libraries https://github.com/funcool/
niwinz 2016-09-20T08:40:52.000046Z

core.async go like macro is coming to funcool/promesa \o/

niwinz 2016-09-20T08:41:57.000047Z

allowing having constructs like this:

(defn my-func
  [i]
  (async
    (loop [sum 0
           c 0]
      (if (< c i)
        (do
          (p/await (p/delay 10))
          (recur (+ sum i) (inc c)))
        sum))))

niwinz 2016-09-20T08:42:34.000048Z

in comparison to the already available alet macro, this one is not limited to let syntax 😄

2016-09-20T12:37:38.000049Z

nice niwinz looks good

2016-09-20T14:54:19.000052Z

Howdy y'all. The basic Buddy auth example for JWE tokens doesn't seem to be working for me. I'm using Boot and System with Sente to send the POST request to login, and it returns success. But I always get unauthorized when I check with buddy/authenticated?

2016-09-20T14:54:44.000053Z

I'm guessing the problem is that Buddy only checks the :identity key in the Ring map, but the example puts the token elsewhere? Ahdunno, tried this for a few hours yesterday.

2016-09-20T14:55:33.000054Z

It's possible to check for the existence of the token wherever it's puts, but I kind of want to use the Buddy middleware because I figure it will easily tell if the token has expired.

donaldball 2016-09-20T14:59:17.000055Z

This may be neither here nor there, but FWIW I couldn’t ever get buddy working with JW* tokens. I ended up writing my own (pedestal) middleware using the nimbus Java library directly.

2016-09-20T15:07:14.000056Z

@donaldball Really good to know it's not just me. I think it indicates there may be a problem at least with the documentation? If I get this figured out I'd be happy to issue a PR or just set up an example repo.

niwinz 2016-09-20T16:20:19.000059Z

@coyotespike @donaldball I'm glad to help you but I need a more concrete example of that is not working. Take care that buddy conciselly separates the authorization process from the authentication.

niwinz 2016-09-20T16:20:47.000060Z

The documentation explains exactly steps on how the backend works and where the authentication is done and how the authorization is working

niwinz 2016-09-20T16:21:29.000061Z

if buddy/authenticated? returns false, it means that no one backend have obtained valid data from the request in order to authenticate it...

niwinz 2016-09-20T16:22:12.000062Z

if backend authenticates the request, it is responsible to attach the :identity keyword to it, in order to buddy/authenticated? will be able to return true...

niwinz 2016-09-20T16:23:39.000063Z

I'm myself using buddy in many projects, and everything is working as expected. You thing that something is not working as expected I'm gladly fix that!

2016-09-20T16:25:41.000064Z

Hi @niwinz! Thanks very much, any hints would be fantastic. I've read the documentation and the source (as best I can), and read every repo example I could find. I'd be happy to provide my repo and the relevant handlers.clj file if that would be helpful - it's not a large repo.

2016-09-20T16:26:17.000065Z

Of course, just because it's not working the way I expected, doesn't mean buddy's not working correctly! 😉

niwinz 2016-09-20T16:26:28.000066Z

@coyotespike repo access seems like the fastest way 😉

2016-09-20T16:26:37.000067Z

Fantastic, brb

2016-09-20T16:29:32.000070Z

Lines 80-100 have the login function and routes.

niwinz 2016-09-20T16:31:51.000071Z

Hmm, the fist strange thing that I see is the use of session...

niwinz 2016-09-20T16:32:05.000072Z

but on the end you are not using any ring session middleware...

niwinz 2016-09-20T16:32:32.000073Z

and the combination of the session and the JWT...

niwinz 2016-09-20T16:32:52.000074Z

seems like there are a lot of confusion.

niwinz 2016-09-20T16:33:04.000075Z

JWT is designed primarily to does not have any session.

niwinz 2016-09-20T16:33:11.000076Z

and is designed to be used with api based backends

2016-09-20T16:33:58.000078Z

Hmm, it sounds like I am confused.

niwinz 2016-09-20T16:34:18.000079Z

Ok, I'll try to explain step by step

niwinz 2016-09-20T16:34:21.000080Z

how it works

2016-09-20T16:34:52.000081Z

Thank you. I think I need to use wrap-defaults with the Sente library, but if that interferes with Buddy...

niwinz 2016-09-20T16:35:09.000082Z

It not interfers

niwinz 2016-09-20T16:35:56.000083Z

in fact buddy works with any middleware and is conflictless with others middlewares

niwinz 2016-09-20T16:36:51.000084Z

The first step is where the client makes an ajax call to the login endpoint and the login should return the token in the body...

niwinz 2016-09-20T16:37:28.000085Z

The second step is that the client persists that token in some storage in the browser

niwinz 2016-09-20T16:37:57.000086Z

and later perform any call to any other endpoint with token in the header Authorization

niwinz 2016-09-20T16:37:59.000087Z

with value

niwinz 2016-09-20T16:38:08.000088Z

Bearer 5985hjejcdirh58fcjtnfcisdjti (random token...)

2016-09-20T16:39:00.000090Z

Ahh. I was under the impression that Buddy would find the token where we put it server-side. However, actually the client must take the token and put it in the Authorization Header

niwinz 2016-09-20T16:39:02.000091Z

Then in, this requests, buddy parses that requests and obtains that token, validates it and in case of that token is properly validated, attaches to the request map the :identity keyword

niwinz 2016-09-20T16:39:21.000092Z

this is how works JWT...

niwinz 2016-09-20T16:39:30.000093Z

later you has the session backend

niwinz 2016-09-20T16:39:43.000094Z

that works with session in server side

niwinz 2016-09-20T16:40:27.000095Z

in that case, the developer responsability is just login the user in the login endpoint and associate :identity keyword to the ring session

niwinz 2016-09-20T16:41:01.000096Z

and on the following requests, buddy session backend will detect that the session is authenticated

niwinz 2016-09-20T16:41:45.000097Z

everything is depends on that you are need for the application you are developing... buddy offers a foundation to buid any authentication/authorization mechanism on top of ring

niwinz 2016-09-20T16:43:24.000098Z

but is a little bit more low-level that integrated frameworks and can be a little bit difficult understand on the beginning but its simplicity makes it very flexible in order to handle very different use cases.

2016-09-20T16:43:57.000099Z

The simplicity is why I strongly prefer to use Buddy over Friend, etc!

2016-09-20T16:46:03.000100Z

So our client-side call could look like:

(defn loggedin []
  (sente/ajax-lite "/home"
                   {:method :get
                    :headers {:X-CSRF-Token (:csrf-token @chsk-state)
                              :Authorization "token-here"
                              }}

                   #(js/console.log "CALLBACK from server: " (pr-str %))))

2016-09-20T16:46:56.000101Z

I'm not sure I follow what you mean by "associate :identity keyword to the ring session"

2016-09-20T16:48:13.000102Z

If Buddy finds the token in the Authorization header and puts it in the :identity keyword, does the developer later do something with the :identity keyword?

niwinz 2016-09-20T16:49:56.000103Z

1. "token-here" should be "Bearer token-here"

niwinz 2016-09-20T16:50:28.000104Z

2. when buddy finds the token in the authorization header it associates the :identity keyword with the decrypted value of the JWT token...

niwinz 2016-09-20T16:50:57.000105Z

having :identity with a logical true (anything different to nil and false) makes the request authenticated

niwinz 2016-09-20T16:51:13.000106Z

so the buddy/authenticated? predicate will return true for that requests

2016-09-20T16:56:48.000107Z

I think that makes sense, and I really appreciate your taking the time to walk through it step by step!

niwinz 2016-09-20T16:58:22.000108Z

no problem 😉 glad to help

2016-09-20T16:58:52.000109Z

If it's all right with you I'd love to work on it some more, and post it here if you have time for any more feedback

2016-09-20T17:00:07.000110Z

This is one of the last steps of rolling my own framework with my perfect stack: Buddy, System, Sente, Boot, DevCards, re-frame.

niwinz 2016-09-20T17:02:26.000111Z

Feel free to do it. If I will have time to review it, I'm gladly will do it 😉

niwinz 2016-09-20T17:28:17.000112Z

1.6.0-SNAPSHOT of funcool/promesa is released with the new async macro

niwinz 2016-09-20T17:28:26.000113Z

usage is documented in tests at this moment: https://github.com/funcool/promesa/blob/master/test/promesa/core_tests.cljc#L328

niwinz 2016-09-20T17:28:40.000115Z

for any one that want experiment with it 😉

2016-09-20T18:28:42.000117Z

@niwinz very happy to say I finally got it working! In the next couple of weeks I'll try to make a minimal example of a roundtrip for other n00bs. 😎

2016-09-20T18:30:10.000119Z

I put the token in :uid, because Sente maintains that as an atom. And sent it back like:

(sente/ajax-lite "/home"
                   {:method :get
                    :headers {:X-CSRF-Token (:csrf-token @chsk-state)
                              "Authorization" (str "Token " (:uid @chsk-state))
                              }}

2016-09-20T18:30:40.000120Z

(str "Bearer " (:uid @chsk-state)) didn't work, for some strange reason.

2016-09-20T18:34:26.000121Z

One other thing that surprised me. I also just got it working with Liberator. To use Liberator's :authorized? key, I have to pull the :request out of the map to pass it to Buddy's :authenticated? function.

2016-09-20T18:34:29.000122Z

Like so:

2016-09-20T18:34:38.000123Z

(defresource mustbeloggedin [ctx]
  :authorized? (fn [ctx] (authenticated? (:request ctx)))
  :available-media-types ["text/plain" "application/clojure;q=0.9"]
  :allowed-methods [:get :post]
  :handle-ok (fn [ctx] {:yourock "no, really"}))

2016-09-20T18:34:59.000124Z

where ctx is just the Ring request map.

2016-09-20T18:35:40.000126Z

Thanks very much for your patience and help @niwinz!

mattly 2016-09-20T20:16:17.000127Z

ooh the async macro looks nice