Friend authentication empty param - clojure

I am trying to implement friend authentication on my web app but when i try to login i get this */login?&login_failed=Y&username=*...my param is empty and i cant login,what am i doing wrong?
these are my routes...
(defroutes routes
(GET "/" [] (index))
(GET "/indexMessage" [] (indexMessage))
(GET "/login" req (index))
(POST "/insert-user" {params :params}
(let [firstname (get params "firstname")
lastname (get params "lastname")
email (get params "email")
password (get params "password")
sex (get params "sex")
year (get params "year")
month (get params "month")
day (get params "day")]
(def date (str year"-"month"-"day))
(insert-user firstname lastname email password sex date)))
(route/resources "/public")
(route/not-found "Page not found")
)
i used all the middleware needed...
(def page (handler/site
(friend/authenticate
routes
{:allow-anon? true
:login-uri "/login"
:default-landing-uri "/"
:unauthorized-handler #(-> (html5 [:h2 "You do not have sufficient privileges to access " (:uri %)])
resp/response
(resp/status 401))
:credential-fn #(creds/bcrypt-credential-fn #users %)
:workflows [(workflows/interactive-form)]})
(wrap-keyword-params routes)
(wrap-nested-params routes)
(wrap-params routes)
(wrap-session routes)
))
and this is how i start up my jetty server...
(defn -main []
(run-jetty page {:port 8080 :join? false}))
users is a map like this...
{"username" {:username "username" :password "password"}}
is :roles a must in the map?maybe it's because of that?

I am pretty new to Friend as well but from the Friend source code I can say that the parameters name of your POST request matters. I guess you are following this example, if not, it's the best hint you can get actually. Notice the name of the form fields
https://github.com/cemerick/friend-demo/blob/master/src/clj/cemerick/friend_demo/interactive_form.clj#L22-l24
All credential functions take a single argument, a map containing the available credentials, so
as there is no explicit POST "/login" route, the Friend midleware is catching and using them as credentials for your credential-fn as shown here https://github.com/cemerick/friend/blob/master/src/cemerick/friend/workflows.clj#L76-78
So "username" and "password" should be the names of the parameters POSTed to the :login-uri
For newcomers that example is runnable here http://friend-demo.herokuapp.com/interactive-form/

Related

How to save a user in noir session in a compojure web project?

I have to save a username in a session and I can't seem to make it work. This is my code (sesh is noir session):
(defn set-loggedin [username password]
(do
(sesh/put! :username (:user username))
(sesh/put! :password (:pass password))))
(defn login-handler [username password]
(let [user (datab/login username password)]
(if (empty? user)
(view/login-form "Wrong username/password")
(do
(set-loggedin username password)
(response/redirect "/main")))))
(defroutes app-routes
(GET "/" [] (view/login-form "" ))
(POST "/" [username password] (login-handler username password))
(GET "/main" [] (view/main-page))
(route/resources "/")
(route/not-found "Not Found"))
(def app
(sesh/wrap-noir-session
(wrap-defaults app-routes (assoc-in site-defaults [:security :anti-forgery]
false))))
This is the main page in hiccup and it doesn't show the user when I login:
(defn main-page []
(html[:head [:title "main"]] [:body
(sesh/get :username)] ))
Any help would be appreciated!
In your set-loggedin function, you are trying to get the :user key from 'username', which I suspect is a string and the :pass key from 'password', which is probably a string.
The do is also unnecessary in a function definition so try this:
(defn set-loggedin [username password]
(sesh/put! :username username)
(sesh/put! :password password)))

Why do I get no username/password dialog when using buddy-auth?

I'm using buddy-auth and following the tutorial, when I run the example application I get a HTTP auth username/password dialog as expected, but in my own app I just get the "Unauthorized" exception, no dialog appears.
;; fn needs to return a non-falsey value to indicate a positive authentication, the returned value is stored under the `:identity` key in the request.
(defn auth-user [request authdata]
(let [username (:username authdata)
password (:password authdata)]
username)) ;; FIXME: lookup username/password
(def auth-backend (http-basic-backend {:realm "MyApp" :authfn auth-user}))
;; my endpoint handler
(defn test-handler [r]
(if (authenticated? r)
(render (str "LOGGED IN" (:identity r)))
(throw-unauthorized)))
;; ROUTES (compojure)
(defroutes app
(GET "/test" [] test-handler))
;; ring handler
(def site
(-> (routes app)
(wrap-authentication auth-backend) ;; <---
(wrap-defaults)
(wrap-with-exception-handling)))
I don't think auth-user is ever called.
It seems I need (wrap-authorization auth-backend) too.
(def site
(-> (routes app)
(wrap-authentication auth-backend)
(wrap-authorization auth-backend)
(wrap-defaults)
(wrap-with-exception-handling)))

How to get logged in user's id from session in LuminusWeb

I've just saved the current logged-in user in session. Now how do I get user's id from session in Selmer templates?. Thanks
(defn login-form [{:keys [flash]}]
(layout/render
"login_form.html"
(merge (select-keys flash [:id :pass :errors]))))
(defn authenticate [id pass]
(when-let [user (db/get-user {:id id})]
(when (hashers/check pass (:pass user))
id)))
(defn login! [{:keys [params]}]
(if-let [id (authenticate (:id params) (:pass params))] ;TODO: Refactor
(do
>>>>>>>>> (update (response/found "/") :session {:id id}))
(layout/render "login_form.html" {:invalid-credentials? true})))
(defroutes auth-routes
(GET "/accounts/login/" request (login-form request))
(POST "/accounts/login/" request (login! request)))
Update
I've updated my login function. and it works.
(defn login! [{:keys [params session]}]
(if-let [id (authenticate (:id params) (:pass params))] ;TODO: Refactor
(-> (response/found "/")
(assoc :session
(assoc session :id id)))
(layout/render "login_form.html" {:invalid-credentials? true})))
To print the user's id, I've changed the home-routes.
(defn home-page [opts]
(layout/render "home.html" opts))
(defroutes home-routes
(GET "/" {:keys [flash session]} (home-page (or flash session))))
Now I can print user's id in home.html template. But If I use any other url then user's id stopped `displaying``.
Question > So do I need to pass {:keys [flash session]} in every routes?
You can make use of the wrap-identity middleware which Luminus provides by making sure it's in your wrap-base stack, then change :id to :identity in your session. You can now call (layout/render "home.html") (even without providing opts!) and you can access the identity using {{identity}}

Compojure: access basic-authentication vars in route processing

I am writing a web API using Compojure with basic-authentication middleware. The basic authentication part looks something like:
(defn authenticated? [id pass]
(and (= id "blah")
(= pass "blah"))
id and pass are passed in using the id:pass#website technique. My problem is, I would like to access this id and pass further on down where the routes are processed with the various GET, PUT, POST, etc. headings. But I can't figure out how to do this, and haven't had any luck googling around.
; i'd like to access id and pass here
(defroutes routes
(GET "/" [] {:status 200 :headers {} :body "hi!"})
...)
I presume the solution is for the above to somehow "add" id and pass to some set of variables that can be accessed where the routes are processed, but I have no idea how to add nor to access them.
Hopefully someone can point me in the right direction - thanks.
Assuming you are talking about https://github.com/remvee/ring-basic-authentication, the authenticated? fn can return a truthly value that will be added to the request in the :basic-authentication. Something like (untested):
(defn authenticated? [id pass]
(and (= id "gal")
(= pass "foo")
{:user id :passwd pass}))
(defroutes routes
(GET "/" {the-user :basic-authentication} {:status 200 :headers {} :body (str "hi! Mr. " (:user the-user) " your password is " (:passwd the-user))})
...)
The return of the authenticated? method is associated in request map referenced by the key :basic-authentication. Here goes an example with a route that returns the user. You could, however, return a map or any other object and access it through :basic-authentication key.
(defn authenticated? [user password] user)
(defroutes require-auth-routes
(GET "/return-user" request (.toString (:basic-authentication request)))
(def my-app
(routes
(-> require-auth-routes
(wrap-basic-authentication authenticated?)))

using a custom :store with wrap-multipart-params in ring middle ware

I'm trying to use a custom :store option for wrap-multipart-params and instead I'm clearly getting the default store. My custom function isn't even being called.
(mp/wrap-multipart-params
(POST "/upload-x" request (upload/upload-file request))
{:store upload/logging-store})
My logging store function looks like this (it's just a dummy for now - eventually I want to handle the stream in a custom way) None of that IO happens.
(defn logging-store [{filename :filename
content-type :content-type
stream :stream
:as params}]
(println "in logging store")
(pprint filename)
(pprint params)
filename)
upload-file looks like this:
(defn upload-file [{params :params
session :session :as request}]
(let [user-id (:user-id session)
files (get params "files")]
(pprint request)
(pprint params)
(response/response
{:status :success})))
The printing for the request and the params clearly show the multipart params in there and that they are being handled by the temp file store:
:multipart-params
{"files"
{:size 1674,
:tempfile
#<File /var/folders/rx/9ntjyyvs35qbmcbp6rhfmj200000gn/T/ring-multipart-3853352501927893381.tmp>,
:content-type "application/octet-stream",
:filename "blog-test.clj"}},
EDIT: app definition (as requested)
(defroutes file-list-routes
(GET "/simple-upload" request (upload/simple-upload-file request))
(mp/wrap-multipart-params
(POST "/upload-x" request (upload/upload-file request))
{:store upload/logging-store})
)
(defroutes scratch-app
(context "/files" request file-list-routes)
(route/resources "/")
(route/not-found "Page not found"))
(def my-app
(handler/site
(ring.middleware.json/wrap-json-response
(ring.middleware.json/wrap-json-params
(ring.middleware.stacktrace/wrap-stacktrace
(ring.middleware.session/wrap-session
scratch-app
{:store (ring.middleware.session.memory/memory-store all-the-sessions)
:cookie-attrs {:max-age 3600 ;in s 3600s = 1h
}}))))))
(ring.util.servlet/defservice my-app)
The compojure.handler/site function contains the wrap-multipart-params middleware, so you're unknowingly applying the multipart middleware twice: first with the defaults, then with your custom options. Because the multipart middleware with the default options is applied first, that take priority.