I'm talking to my company's app's api. First I need to get a session token and authenticate, then I can mess around in the repl.
I'd like to know, what is a sane way to run a function that does these things and verifies that I'm authenticated, and then stop worrying about it
here is what I ran in the repl to get authenticated and verify I'd done it right:
(ns api.auth-and-verify
(:require [clj-http.client :as client]
[clj-http.cookies :as cookie]
[cheshire.core :as json]
[slingshot.slingshot :refer [try+]]))
(def cookies (cookie/cookie-store))
(client/get "<http://localhost:8080>" {:cookie-store cookies})
(def token-value
(get-in (cookie/get-cookies cookies) ["XSRF-TOKEN" :value]))
(client/post "<http://localhost:8080/api/v1/proxy/auth/token>" {:cookie-store cookies
:headers {"X-XSRF-TOKEN" token-value}
:form-params {"username" "<mailto:user@company.co|user@company.co>"
"password" "s3cr3t!
(client/get "<http://localhost:8080/api/v1/internal/account/details>" {:cookie-store cookies})
;; => a correct-looking response
does anyone have recommendations on how to organize this so I can call a function once and then have future calls be more about the body of requests and less about the cookie-and-header auth plumbing?
The cookie is state, so you are wanting some kind of state management - is this in the context of a larger app, or just fiddling around in the repl?
I would probably start with writing a small wrapper (defn call-my-api [cookie-store & args]...
which you would call passing a stateful cookie store (probably just an atom if you are just doing repl work, but in the context of an app, a component or some such thing managed by component
, mount
, integrant
etc). that function can check the state of the cookie store to see if you are authed, authenticate if not, and then pass args through to the post
could take it in a few different directions frm there depending on the variety of calls you need to make
but that would be my first general approach
Does the cookie expire? (I assume it does)
We have somewhat similar code in one of the parts of the system which has to refresh API tokens every now and then, we do this by centralizing the token rotation and make all clients aware of the token and pulling it from a shared resource (a Component actually). This way performing requests doesn't involve refreshing the token and obtaining a new one.
> is this in the context of a larger app, or just fiddling around in the repl? Currently fiddling, but with an interest in using a programming language to do some API work and API tests in something other than Postman
Postman is familiar to the rest of my team but, from working with it this week it seems like it would be much better to just use a programming language. I developed the code above in parallel, while I was learning how auth worked at all
The cookie does expire. In my workflow, I restart our app much more often than the cookie expires. Some of the developers may have the same instance of the app running long enough for expiration to be a concern
It does sound like it's time for me to learn one of those component-style frameworks, finally
Try without it for a while, just making the http client call wrapper fn. There is always time to add complexity later :)
yup, and you can handle the time out refreshing within that as well. when the cookie is authed for the first time, you could kick off a little core.async.go-loop
that parks until you hit your timeout threshold, refreshes the token, then loops. for a little more resiliancy you may need to add a little bit of mechanism to hold off on making a call if you are currently refreshing the auth token, so you don't try to send a request at the same time
i'm using stuartsierra.component
currently but I've heard nothing but good things about integrant
, I've been meaning to try it out on a new project for a while. I'd probably start by looking at integrant
as it was built largely in response to some common complaints about component