Ring comes with two session storage engines, one for in-memory
sessions, and one for encrypted cookie sessions.
Memory storage is the default, but you can change the store type by
using the :store option:
(use '[ring.middleware.session memory cookie])
(def default-store-app
(-> handler wrap-session))
(def memory-store-app
(-> handler (wrap-session {:store (memory-store)})))
(def cookie-store-app
(-> handler (wrap-session {:store (cookie-store)})))
Please bear in mind that encryption is easy to get wrong, and whilst
the cookie encryption is secure to the best of my knowledge, I'm not
an cryptography expert.
The cookies are encrypted with 256 bit AES (CBC), with a SHA256 HMAC
using Java's crypto APIs. I've checked for timing attacks against the
HMAC and found none, but that doesn't mean I haven't missed something.
You can also create your own session storage by extending the
SessionStore protocol.
- James
http://mmcgrana.github.com/ring/middleware.session-api.html
And there's an basic example in the Ring wiki:
Additionally, there's a project called Sandbar that adds stateful
session support, should you require it.
http://github.com/brentonashworth/sandbar
- James
(defn auth-handler [code]
{:status 302
:headers {"Location" "/fb"}
:session {:me (get-my-info (get-access-token code))})
You could also use the functions in ring.util.response to help you:
(defn auth-handler [code]
(-> (redirect "/fb")
(assoc-in [:session :me] (get-my-info (get-access-token code)))))
Although I'd be tempted to wrap it in a route:
(defn auth-route [path options]
(GET path {{code "code"} :params, session :session}
(let [session (assoc session :me (get-info code))]
(-> (redirect (options :redirect))
(assoc :session session)))))
Then use it like:
(auth-route "/fb/code" {:redirect "/fb"})
I haven't played around with Facebook authentication before, but it
seems to me that it would be just the kind of thing you'd abstract
away into middleware.
However, if you want something that acts more like a traditional,
stateful session, I'd suggest trying out Sandbar.
- James
To update the session, you need to add the updated session map to the
:session key on your response.
- James
What do you mean "stateless sessions"? The whole point of sessions is
that they maintain state across requests.
- James
- James
It depends, but I'd write some middleware to add the user to a binding. e.g.
(declare *user*)
(defn wrap-user [handler]
(fn [{session :session, :as request}]
(binding [*user* (:user session)]
(handler request))))
- James
It really depends what you want to do.
There is an example in the Ring wiki that demonstrates how to read and
write sessions, and the API documentation for wrap-session is fairly
explicit.
But I guess you're looking for a more specific example. Could you
describe what your application is going to do (with respect to
sessions)?
- James
There are two approaches to this problem. The first is to use Sandbar
to provide stateful sessions:
(defn homepage-handler []
(let [me (session-get :me)]
...))
(wrap! app
(:stateful-session {:store (cookie-store)}))
The other is to create some middleware to abstract authentication:
(declare *me*)
(defn wrap-user-binding [handler]
(fn [{session :session, :as request}]
(binding [*me* (:me session)]
(handler request))))
(wrap! app
:user-binding
(:session {:store (cookie-store)}))
But if you're not confident constructing middleware like this, I'd
advise using Sandbar.
- James