I finally got OAuth workflows working for Twitter and Facebook logins on my Clojure Compojure project, but I need to handle new users. Where in the Friend workflow should I be creating non-existent users in my data store? Inside credential-fn perhaps?
That is correct. The credential-fn from one of my project looks right now something like:
(defn linkedin-auth [{access-token :access-token :as m}]
(let [options {:query-params {:oauth2_access_token access-token
:format "json"}}
account-info (http-json "https://api.linkedin.com/v1/people/~" options)
email (http-json "https://api.linkedin.com/v1/people/~/email-address" options)]
(register-or-load-from-db
{:roles #{::user}
:identity email
:name (str (:firstName account-info) " " (:lastName account-info))}))))
And the workflow config:
(oauth2/workflow {:client-config linkedin-client-config
:uri-config linkedin-uri-config
:credential-fn #'linkedin-auth
:login-uri "/linkedinlogin"})
Related
I am trying to access my Swagger service endpoints. Here is the code:
(defn service-routes []
["/api"
{:middleware [middleware/wrap-formats]
:swagger {:id ::api}}
["" {:no-doc true}
["/swagger.json"
{:get (swagger/create-swagger-handler)}]
["/swagger-ui*"
{:get (swagger-ui/create-swagger-ui-handler
{:url "api/swagger.json"})}]]
["/business-partners"
{:get
(fn [_]
(response/ok (bp/business-partners-list)))}]
["/business-partner"
{:post
(fn [{:keys [params]}]
(try
(bp/save-business-partner params)
(response/ok {:status :ok})
(catch Exception e
(let [{id :business-partners/error-id
errors :errors} (ex-data e)]
(case id
:validation
(response/bad-request {:errors errors})
(response/internal-server-error
{:errors {:server-error ["Failed to save message!"]}}))))))}]])
But when I tried to access http://localhost:3000/api/swagger.json in browser I got this response:
{"swagger":"2.0",
"x-id":["businesspartners.routes.services/api"],
"paths":{
"/api/business-partners": {"get": {}},
"/api/business-partner": {"post": {}}}}
I can't figure out why did I get this in json format and why not see Swagger UI to visaulize and interact with my services? I should get something like this:
I’d imagine you’ll find the swagger ui under /api
I was also facing same issue and using code generation with Java with language jaxrs
Once you generate the code you will see the index.html page generated with endpoints under target/generated-sources/swagger/ folder. Make sure you include it in you build and point api path to this page so that you can view endpoints as html.
I'm using the ring basic-authentication library available for compojure. The authenticated? function takes a username and a password in order to authenticate, but in my particular case I need to access other parameters passed in by the user request besides a username and a password.
For instance, consider a case where there are multiple servers, and a user has an account on a particular server. That user would therefore need to authenticate with (log-on to) a particular server. Therefore I need his username, password, AND server to do the authentication.
Handling a "normal" case which just calls for a username and password might look something like this (using example sql to hit a database):
; curl my_id:my_pass#localhost/my_request
(defn authenticated? [id password]
(first (select user (where {:id id :password password}) (limit 1))))
I'd like to do something like this:
; curl my_id:my_pass#localhost/my_server/my_request
(defn authenticated? [id password ??server??]
(first (select user (where {:server server :id id :password password}) (limit 1))))
I guess my question is, how do I access all request params from inside authenticated? Or, alternatively, how do I pass the server id into the authenticated? function?
Thanks.
In accordance with the comments to the question above, the approach would look like this (just a sketch, I haven't tested whether this works due to lack of a running setup with ring):
(defn wrap-basic-auth-server [handler authfn]
(fn [request]
(-> request
(wrap-basic-authentication (partial authfn (get-in request [:params :server])))
handler)))
What's happening here is that the code is assuming that your server url param will have been added by wrap-params (from ring.middleware.params) to the request map. wrap-basic-authentication is called then with the handler (typical ring middleware, i.e. any other wrapper / handler coming afterwards) and a new (partial) function, which is just your authenticate function which has already swallowed the server arg.
And then instead of just calling wrap-basic-authentication in your routes, you need to add wrap-params and wrap-basic-auth-server and you need your new auth function. E.g.
(defn authenticated? [server user pass]
... your code ...)
(def app
(-> app-routes
... potential other wrappers elided ...
(wrap-params)
(wrap-basic-auth-server authenticated?)))
As I said, I've not tested this code, but it should get you started.
I'm working on a clojure web application project for my college,and i am trying to connect datomic database with friend authentication but is kinda buggy...i will explain further...
First i am doing user registration(user insertion in datomic database) like this and it is working.
(defn insert-user [firstname lastname email password sex date] (.get (.transact conn
[{
:db/id #db/id[:db.part/user -1000001]
:user/name firstname
:user/lastName lastname
:user/username email
:user/password (creds/hash-bcrypt password)
:user/gender sex
:user/birthDate date}
]))
(resp/redirect "/")
)
The routes handler and friend authenticator looks like this...function main is to start the app.
(def page (handler/site
(friend/authenticate
routes
{:allow-anon? true
:login-uri "/login"
:default-landing-uri "/login"
:unauthorized-handler #(-> (html5 [:h2 "You do not have sufficient privileges to access " (:uri %)])
resp/response
(resp/status 401))
:credential-fn (partial creds/bcrypt-credential-fn users)
:workflows [(workflows/interactive-form)]})
(wrap-keyword-params routes)
(wrap-nested-params routes)
(wrap-params routes)
))
(defn -main []
(run-jetty page {:port 8080 :join? false}))
And for the end the datomic query for users to match with creds/bcrypt-credential-fn function of friend.
(defn upit-korisnici []
(def temp (d/q '[:find ?u ?p
:where [?user :user/username ?u]
[?user :user/password ?p]
]
(d/db conn)))
(def users (into {} (map (fn [[k v]] [k {:username k :password v}]) temp)))
users
)
The thing that is bugging me and leaving me helpless is that when i register(insert user),the user is inserted in datomic database but when i try to log in i can't.It says wrong email and password but the new user is there.When i restart the whole app and try to login with the new users credentials it goes through and logs on.Does anyone know how to solve this problem?
Edit:
I solved this problem by changing :credential-fn (partial creds/bcrypt-credential-fn users) to :credential-fn #(creds/bcrypt-credential-fn users %).
You seem to think it will automatically update your user data, but it isn't going to because user is not a function it is plain data. What happens is (def user... is run then the results are bound to the name user, you are not binding the computation so the data is never updated. You make a similar mistake for temp. The query is run once then the results are bound to temp, and never reevaluated. You should bind them to a function so that it revaluates.
I started working on an UI for the friend lib with datomic persistence: https://github.com/sveri/friend-ui/ You can have a look at it, maybe it solves your problem already, of course you can take code from it, add pull requests / whatever. When I have time I will implement whatever is needed.
Currently it supports:
Sign up
Login
Logout
Twitter Bootstrap support for the templates
It would be nice if we could bundle the work done, as this will be something that many people will need in future.
I'm not really understanding https://github.com/clojure-liberator/liberator and the list of decision points that it provides to the developer. How would one implement a basic auth/auth service using/alongside/on-top-of the library?
The idiomatic way is to implement the :authorized? decision point. However there is currently no support for the handling of basic or digest authentication. A practical approach is to use ring-basic-authentication for authentication and handle only authorization in the resource. The following example uses ring-basic-authentication and sets the token to a users's role. This role is then checked by liberator in authorized?
(defresource admin-only
:handle-ok "secrect"
:handle-unauthorized "for admins only"
:authorized? (fn [{{token :token} :request}]
(= "admin" token)))
;; token returned encodes role
(defn authenticated? [name pass]
(cond (and (= name "scott")
(= pass "tiger")) "admin")
(and (= name "jack")
(= pass "jill")) "user)))
(def app (wrap-basic-authentication admin-only authenticated?))
from the readme"
Resources are compatible with Ring and can be wrapped in Ring middleware. When evaluated, a resource returns a function which takes a Ring request and returns a Ring response.
so you can then wrap it in ring-basic-authentication
(use 'ring.middleware.basic-authentication)
(defn authenticated? [name pass] (and (= name "foo") (= pass "bar")))
(def app (-> routes .. (wrap-basic-authentication authenticated?))
I've just seen this library https://github.com/technomancy/clojure-http-client with this snippet of code on the README page which is what I'm looking to do
(res/with-cookies {}
(res/post "http://localhost:3000/login" {} {"user" user "password" password})
(res/get "http://localhost:3000/my-secret-page))
However it appears that the lib is deprecated and it advises you to use the clj-http library instead. I'm just wondering if anyone knows how to replicate this sort of behaviour using that library?
At the moment I just do
(post "<site i want to login to>" {:form-params {:username "<my username>" :password "<my password>"}})
Which returns a cookie that has a http 302 redirect to the authenticated page, but I have no idea how to make the client follow this redirect using the authenticated cookie
Any help would be appreciated.
FYI I resolved this,
(defn login [login-url user pass]
(let [result (client/post "http://my-site.com/login" {:form-params {:username user :password pass}})]
(when (= (:status result) 302)
(:cookies result))))
Should login be successful it will return a cookie map, this can then be used in subsequent requests when visiting pages that require you to be logged in, e.g.
(when-let [cookies (login "http://my-site.com" "my-user" "my-pass")]
(client/get "http://my-site.com/user-page" { :cookies cookies }))
=> <html><head><title>Hello my-user!</titl.......
I guess you need to explicitly use follow-redirect function from the library.