Fork me on GitHub
#funcool
<
2016-09-20
>
niwinz08:09:52

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

niwinz08:09:57

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

niwinz08:09:34

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

iwillig12:09:38

nice niwinz looks good

coyotespike14:09:19

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?

coyotespike14:09:44

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.

coyotespike14:09:33

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.

donaldball14:09:17

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.

coyotespike15:09:14

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

niwinz16:09:19

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

niwinz16:09:47

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

niwinz16:09:29

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

niwinz16:09:12

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

niwinz16:09:39

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!

coyotespike16:09:41

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.

coyotespike16:09:17

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

niwinz16:09:28

@coyotespike repo access seems like the fastest way 😉

coyotespike16:09:37

Fantastic, brb

coyotespike16:09:32

Lines 80-100 have the login function and routes.

niwinz16:09:51

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

niwinz16:09:05

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

niwinz16:09:32

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

niwinz16:09:52

seems like there are a lot of confusion.

niwinz16:09:04

JWT is designed primarily to does not have any session.

niwinz16:09:11

and is designed to be used with api based backends

coyotespike16:09:58

Hmm, it sounds like I am confused.

niwinz16:09:18

Ok, I'll try to explain step by step

niwinz16:09:21

how it works

coyotespike16:09:52

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

niwinz16:09:09

It not interfers

niwinz16:09:56

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

niwinz16:09:51

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

niwinz16:09:28

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

niwinz16:09:57

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

niwinz16:09:59

with value

niwinz16:09:08

Bearer 5985hjejcdirh58fcjtnfcisdjti (random token...)

coyotespike16:09:00

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

niwinz16:09:02

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

niwinz16:09:21

this is how works JWT...

niwinz16:09:30

later you has the session backend

niwinz16:09:43

that works with session in server side

niwinz16:09:27

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

niwinz16:09:01

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

niwinz16:09:45

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

niwinz16:09:24

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.

coyotespike16:09:57

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

coyotespike16:09:03

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

coyotespike16:09:56

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

coyotespike16:09:13

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?

niwinz16:09:56

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

niwinz16:09:28

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

niwinz16:09:57

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

niwinz16:09:13

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

coyotespike16:09:48

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

niwinz16:09:22

no problem 😉 glad to help

coyotespike16:09:52

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

coyotespike17:09:07

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

niwinz17:09:26

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

niwinz17:09:17

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

niwinz17:09:40

for any one that want experiment with it 😉

coyotespike18:09:42

@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. 😎

coyotespike18:09:10

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

coyotespike18:09:40

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

coyotespike18:09:26

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.

coyotespike18:09:38

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

coyotespike18:09:59

where ctx is just the Ring request map.

coyotespike18:09:40

Thanks very much for your patience and help @niwinz!

mattly20:09:17

ooh the async macro looks nice