core.async go like macro is coming to funcool/promesa \o/
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))))
in comparison to the already available alet
macro, this one is not limited to let syntax 😄
nice niwinz looks good
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?
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.
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.
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.
@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.
@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.
The documentation explains exactly steps on how the backend works and where the authentication is done and how the authorization is working
if buddy/authenticated? returns false, it means that no one backend have obtained valid data from the request in order to authenticate it...
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
...
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!
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.
Of course, just because it's not working the way I expected, doesn't mean buddy's not working correctly! 😉
@coyotespike repo access seems like the fastest way 😉
Fantastic, brb
Here is the handler file: https://github.com/coyotespike/holygrailreagent/blob/master/src/clj/holy_grail/handler.clj
Lines 80-100 have the login function and routes.
Hmm, the fist strange thing that I see is the use of session...
but on the end you are not using any ring session middleware...
and the combination of the session and the JWT...
seems like there are a lot of confusion.
JWT is designed primarily to does not have any session.
and is designed to be used with api based backends
Hmm, it sounds like I am confused.
Ok, I'll try to explain step by step
how it works
Thank you. I think I need to use wrap-defaults
with the Sente library, but if that interferes with Buddy...
It not interfers
in fact buddy works with any middleware and is conflictless with others middlewares
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...
The second step is that the client persists that token in some storage in the browser
and later perform any call to any other endpoint with token in the header Authorization
with value
Bearer 5985hjejcdirh58fcjtnfcisdjti
(random token...)
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
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
this is how works JWT...
later you has the session backend
that works with session in server side
in that case, the developer responsability is just login the user in the login endpoint and associate :identity
keyword to the ring session
and on the following requests, buddy session backend will detect that the session is authenticated
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
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.
The simplicity is why I strongly prefer to use Buddy over Friend, etc!
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 %))))
I'm not sure I follow what you mean by "associate :identity
keyword to the ring session"
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?
1. "token-here" should be "Bearer token-here"
2. when buddy finds the token in the authorization header it associates the :identity keyword with the decrypted value of the JWT token...
having :identity with a logical true (anything different to nil
and false
) makes the request authenticated
so the buddy/authenticated?
predicate will return true
for that requests
I think that makes sense, and I really appreciate your taking the time to walk through it step by step!
no problem 😉 glad to help
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
This is one of the last steps of rolling my own framework with my perfect stack: Buddy, System, Sente, Boot, DevCards, re-frame.
Feel free to do it. If I will have time to review it, I'm gladly will do it 😉
1.6.0-SNAPSHOT of funcool/promesa is released with the new async
macro
usage is documented in tests at this moment: https://github.com/funcool/promesa/blob/master/test/promesa/core_tests.cljc#L328
for any one that want experiment with it 😉
@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. 😎
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))
}}
(str "Bearer " (:uid @chsk-state))
didn't work, for some strange reason.
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.
Like so:
(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"}))
where ctx
is just the Ring request map.
Thanks very much for your patience and help @niwinz!
ooh the async macro looks nice