Open GetContented opened 10 years ago
As I've been working on this this further and using it more, I realised it still redirects on unauthorized requests, which isn't what I want. Is there an easy way to make it not do redirects AT ALL? It seems to be kind of baked in at a low level that redirects would be expected.
My aim is to have my single page application (writte in Om, of course) that does requests to the server, and if the user has been timed out, then it should receive an unauthorised response from the server. The SPA will then dutifully drop its state and request the user log back in again, at which point it'll reload its state back to where it was when the user was logged out by the server (timeout).
Any help would be greatly appreciated :) <3
How are you using the workflow? Here's what I have in my single-page app that doesn't redirect on unauthorized requests.
(friend/authenticate ... {:allow-anon? true
:unathorized-handler (constantly {:status 401})
:unauthenticated-handler (constantly {:status 401})
:workflows [...]})
Then your workflow can just return nil on auth failure.
Wow. That looks elegant! :) I was under the impression you had to have a workflow, and none of the existing ones fit, so I wrote my own (above). I'm using it in a ring middleware that I'm calling "auth":
(defn auth
[handler]
(friend/authenticate
handler
{:credential-fn (partial creds/bcrypt-credential-fn users)
:workflows [(api-workflow)]
:login-uri "/spicyapp/login"
:redirect-on-auth? false
:login-failure-handler (fn [req] {:body "invalid username or password" :status 401})}))
What does :allow-anon? do? I'll have a look in the source... nowhere in the doc does it say you can use :unauthorized-handler or :unauthenticated-handler ! :) haha... I only noticed them from reading the source. It'd be nice if they were in the doc.
Oh I should probably mention that I have wrap-edn-params in my middleware stack:
; using ring.middleware.defaults/wrap-defaults
; ring.middleware.edn/wrap-edn-params
; and compojure routes
(def app
(-> (routes home-routes app-routes)
(auth)
(wrap-edn-params)
(wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))
I'm using the interactive form workflow with a similar configuration to Julian for my Single Page. The thing that's missing for me is that after a successful authorization the server will send a 303 code no matter what and I would rather prefer to send a 200 with user profile as a response. I've tried a wrapper around the default interactive-workflow without much success, the response seems to be generated in another point. Anybody knows how to modify the response to a login request?
Yeah I had 303's for a couple of minutes there when I was devving this. My app has a modified response pretty much exactly what you described... it simply sends back the user profile as you want. This is up to your routes... the response from the route that accepts the login data POST request is the one that should be sending back, assuming success. If you're using liberator, it's the :handle-created key/val pair.
Hello Julian, Since the route for login seems to be setup by friend automatically how do you modify it? Can you post an example so I can grep how to do it? I'm a total noob to Clojure web development and ritz so I'm still struggling around it's concepts (btw, I'm not using liberator, will take a look at it)...
Take a look above at my code... you can specify it with the key :login-uri in the map passed to the authenticate fn. By the way I recommend reading the web development with closure book and following along if you haven't. It puts a lot of things into perpective that otherwise can take a while to understand.
Thanks Julian for your pointers. For other people trying the same, I managed to avoid the redirect using the standard interactive form with the following configuration (so I do not in fact use Julian's code):
(defroutes routes
(GET "/" [] (do (println "hi") (index "page")))
(POST "/login" request (generate-response (get-profile request))))
(def app
(-> routes
wrap-edn-params))
(def secured-app
(handler/site (wrap-edn-params
(friend/authenticate app {:allow-anon? true
:redirect-on-auth? false
:login-failure-handler (fn [e] (generate-response {:error "Wrong credentials"} 401))
:unauthenticated-handler #(auth-failure-handler % "unauthenticated");; (fn [e]
:login-uri "/login"
:default-landing-uri "/"
:unauthorized-handler #(auth-failure-handler % "unauthorized");; (fn [e]
:credential-fn (fn[c]
(println "Credential: " c)
(creds/bcrypt-credential-fn users c))
:workflows [ (workflows/interactive-form :redirect-on-auth? false) ]
})
)))
auth-failure-handler is just a helper functions that give the client more information in EDN format about what went wrong (returns a HTTP 401) in my problem domain.
generate-reponse wraps the answer in EDN.
What on earth does allow-anon do? I couldn't really tell so far from people's comments, dox, or the codebase... :(
@JulianLeviston allow-anon? true
will allow unauthenticated users to access resources wrapped by the authenticate
middleware; this allows you to wrap an entire app with the middleware (including e.g. a login page/resource that anonymous users have to be able to hit) and control access strictly with authorization assertions.
Redirect-less API logins should be reasonably straightforward to set up using the interactive-form workflow as others have suggested. If you're looking to modify the ring response sent by it (or any other friend workflow), you'll technically have to create your own workflow, though that's not as onerous as it might sound: remember, workflows are just ring handlers that can optionally return authentication maps, so your own workflow will just be another bit of ring middleware that happens to be aware of that authentication data.
@cemerick Thanks for your response... Perhaps your description of "allow-anon?" should go in the docs somewhere, along with a note about how to set up a non-redirecting workflow. I still find your description a bit confusing. I thought wrapping the entire app with the middleware and controlling access with auth assertions (friend/authorize wrapping each individual route?) was the entire point of friend in the first place? Odd... I'd love to know more about the differences. I think perhaps I need to take another look into the source.
As you can see, I already rolled my own workflow (which I pasted above). It wasn't hard once I read and understood the friend source code. It's not exactly easy to get set up, though. I'm probably naïve, so my apologies for being a rude noob! I appreciate your project very very much. Simple for an average clojurian maybe... but I've only been one professionally for the last two weeks... :)
Hi,
I have a use-case for a new type of workflow which is for single page applications. These require user/pass combo through a http post, but there's no form or redirection involved (kind of a hybrid of http-basic and interactive-form in a way).
So, I wrote the following code. It's not a pull request because I don't have it in a separate repo or anything, and it's lifted straight from my source. Maybe it could be adjusted and added to the main repo as workflows/api?
I hope I haven't done anything crap by submitting this. I just found this incredibly useful and it may be to others, too.