Cookies Example?

258 views
Skip to first unread message

Paul

unread,
Oct 5, 2010, 10:46:51 AM10/5/10
to Ring
Hi,

I'm new to Clojure and Ring/Compojure but have managed to get a basic
web app running using the articles on the web and help from groups,
such as this.

As I'll be deploying on Google App Engine, my current session
management will not work and instead I'd like to havestateless
sessions using (ideally encrypted) cookies.

Can anyone give an example of how to create/encrypt/read cookies?

Many thanks,

Paul.

James Reeves

unread,
Oct 5, 2010, 6:47:44 PM10/5/10
to ring-c...@googlegroups.com
On 5 October 2010 15:46, Paul <spbo...@googlemail.com> wrote:
> As I'll be deploying on Google App Engine, my current session
> management will not work and instead I'd like to havestateless
> sessions using (ideally encrypted) cookies.
>
> Can anyone give an example of how to create/encrypt/read cookies?

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

Paul

unread,
Oct 6, 2010, 5:22:28 AM10/6/10
to Ring
Many thanks James.

Using this method, how do I add/read/remove items from the session?

P.

On Oct 5, 11:47 pm, James Reeves <jree...@weavejester.com> wrote:

James Reeves

unread,
Oct 6, 2010, 8:26:40 AM10/6/10
to ring-c...@googlegroups.com
The API docs explain the wrap-session function in more detail:

http://mmcgrana.github.com/ring/middleware.session-api.html

And there's an basic example in the Ring wiki:

http://gist.github.com/608048

Additionally, there's a project called Sandbar that adds stateful
session support, should you require it.

http://github.com/brentonashworth/sandbar

- James

Paul

unread,
Oct 6, 2010, 9:57:51 AM10/6/10
to Ring
James,

Thanks for the reply, but as a new Clojure user I'm still a little
confused:

I have a handler function:

(defn auth-handler
[code]
(write-session session :me (get-my-info (get-access-token code)))
{:status 302 :headers {"Location" "/fb/"}}
)

This is called from my routes:

(defroutes quote-routes
(GET "/fb/auth/" [code] (auth-handler code))
(route/not-found "Page not found"))

The routes are wrapped thus:

(def app
(-> quote-routes (wrap-session {:store (cookie-store)})))

(defservice app)

My question is, how can I get access to the session from within my
auth-handler?

Many thanks for you patience and help.

Regards,

Paul.

On Oct 6, 1:26 pm, James Reeves <jree...@weavejester.com> wrote:
> The API docs explain the wrap-session function in more detail:
>
> http://mmcgrana.github.com/ring/middleware.session-api.html
>
> And there's an basic example in the Ring wiki:
>
> http://gist.github.com/608048
>
> Additionally, there's a project called Sandbar that adds stateful
> session support, should you require it.
>
> http://github.com/brentonashworth/sandbar
>
> - James
>

Seth Buntin

unread,
Oct 6, 2010, 10:40:26 AM10/6/10
to Ring
Paul,

In your auth handler response add:

:session {:me (get-my-info (get-access-token code))}

So it will look like:

{:status 302 :session {:me (get-my-info (get-access-token
code))} :headers {"Location" "/fb/"}}

The store protocol will take care of running write session
accordingly.

--
Seth

James Reeves

unread,
Oct 6, 2010, 10:57:26 AM10/6/10
to ring-c...@googlegroups.com
Using the standard Ring session middleware, you'd write it something like this:

(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

Paul

unread,
Oct 6, 2010, 11:02:47 AM10/6/10
to Ring
And how to I access the :session in my handler?

I'm still rather confused!

P.

Paul

unread,
Oct 6, 2010, 11:05:39 AM10/6/10
to Ring
I had sandbar working fine, but I had to remove it as I'm deploying on
Google App Engine which ideally needs stateless sessions.

P.

On Oct 6, 3:57 pm, James Reeves <jree...@weavejester.com> wrote:
> Using the standard Ring session middleware, you'd write it something like this:
>
> (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
>

James Reeves

unread,
Oct 6, 2010, 11:10:31 AM10/6/10
to ring-c...@googlegroups.com
The session is stored in a :session key on the request map.

To update the session, you need to add the updated session map to the
:session key on your response.

- James

James Reeves

unread,
Oct 6, 2010, 11:11:57 AM10/6/10
to ring-c...@googlegroups.com
On 6 October 2010 16:05, Paul <spbo...@googlemail.com> wrote:
> I had sandbar working fine, but I had to remove it as I'm deploying on
> Google App Engine which ideally needs stateless sessions.

What do you mean "stateless sessions"? The whole point of sessions is
that they maintain state across requests.

- James

Paul

unread,
Oct 6, 2010, 11:13:24 AM10/6/10
to Ring
I still don't understand how I get hold of this in my handler -
the :session key is empty.

What code do I need to add here:

(defn homepage-handler
"Displays the homepage for the logged-in user"
[]
......)

Thanks.

P.

On Oct 6, 4:10 pm, James Reeves <jree...@weavejester.com> wrote:
> The session is stored in a :session key on the request map.
>
> To update the session, you need to add the updated session map to the
> :session key on your response.
>
> - James
>

Paul

unread,
Oct 6, 2010, 11:15:21 AM10/6/10
to Ring
What I meant was that the state cannot be held on the server, as with
App Engine Google automatically scale and move you onto other servers
as necessary. State may therefor be lost between browser calls.

P.

On Oct 6, 4:11 pm, James Reeves <jree...@weavejester.com> wrote:

James Reeves

unread,
Oct 6, 2010, 11:42:31 AM10/6/10
to ring-c...@googlegroups.com
As far as I'm aware, Sandbar uses the same session storage engine
protocol as Ring, so just use Sandbar with the cookie storage engine.

- James

James Reeves

unread,
Oct 6, 2010, 11:44:57 AM10/6/10
to ring-c...@googlegroups.com
On 6 October 2010 16:13, Paul <spbo...@googlemail.com> wrote:
> I still don't understand how I get hold of this in my handler -
> the :session key is empty.
>
> What code do I need to add here:
>
> (defn homepage-handler
>        "Displays the homepage for the logged-in user"
>        []
>        ......)

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

Paul

unread,
Oct 6, 2010, 12:00:29 PM10/6/10
to Ring
The concept of clojure 'middleware' is too advanced for a beginner
like myself, so unfortunately this goes right over my head.

What would be really useful is a complete compojure+hiccup session
example, if anyone would care to oblige?

Many thanks for all the help given.

P.

On Oct 6, 4:44 pm, James Reeves <jree...@weavejester.com> wrote:

James Reeves

unread,
Oct 6, 2010, 12:19:42 PM10/6/10
to ring-c...@googlegroups.com
On 6 October 2010 17:00, Paul <spbo...@googlemail.com> wrote:
> The concept of clojure 'middleware' is too advanced for a beginner
> like myself, so unfortunately this goes right over my head.
>
> What would be really useful is a complete compojure+hiccup session
> example, if anyone would care to oblige?

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

Paul

unread,
Oct 6, 2010, 12:31:23 PM10/6/10
to Ring
I'd just like to be able to set session parameters in one handler, and
read them in another.

As the example uses a 'lower level' API (i.e. no compojure or hiccup)
I cannot see how I need to change my code to enable sessions.

I'm using very basic compojure+routes+hiccup, and a simple working
example would be a life-saver.

P.

On Oct 6, 5:19 pm, James Reeves <jree...@weavejester.com> wrote:

Brenton

unread,
Oct 6, 2010, 12:38:04 PM10/6/10
to Ring
Paul,

Sandbar's "stateful sessions" are just another way of working with
Ring's session mechanism. The term "stateful" has more to do with the
style of programming than with how the data is stored. When using
"stateful sessions" your functions will have side effects. When using
Ring's method, you will still have pure functions. That is the main
difference. The session data in each case is stored in the backing
store that you configure. The default being the memory store. For
deploying to GAE, you can either use the cookie storage engine or you
could create an implementation of SessionStore that puts data in the
GAE cloud. The second option would not be difficult as you can see
here:

http://github.com/mmcgrana/ring/blob/master/ring-core/src/ring/middleware/session/memory.clj

It is a very simple interface to implement.

With either approach you could still use Sandbar if that is easier for
you.

Here is an example application that demonstrates using sessions in
both ways.

http://bit.ly/bKvBd7

And here is another example that shows how to configure an alternative
storage engine.

http://github.com/brentonashworth/sandbar/blob/master/src/sandbar/example/session_with_reload_demo.clj

In both cases, if you are not using Sandbar, you can replace wrap-
stateful-session with wrap-session.

Brenton

On Oct 6, 8:42 am, James Reeves <jree...@weavejester.com> wrote:
> As far as I'm aware, Sandbar uses the same session storage engine
> protocol as Ring, so just use Sandbar with the cookie storage engine.
>
> - James
>

Paul

unread,
Oct 6, 2010, 1:16:41 PM10/6/10
to Ring
OK, I've managed to piece together the pieces, and got it working!

What I was missing was the following:

(defn homepage-handler
[request]
(let [me (-> request :session :me)
.....

...with this in the route:

(defroutes quote-routes
(GET "/fb/" request (homepage-handler request))

Many thanks for everybody's help.

P.

On Oct 6, 5:38 pm, Brenton <bashw...@gmail.com> wrote:
> Paul,
>
> Sandbar's "stateful sessions" are just another way of working with
> Ring's session mechanism. The term "stateful" has more to do with the
> style of programming than with how the data is stored. When using
> "stateful sessions" your functions will have side effects. When using
> Ring's method, you will still have pure functions. That is the main
> difference. The session data in each case is stored in the backing
> store that you configure. The default being the memory store. For
> deploying to GAE, you can either use the cookie storage engine or you
> could create an implementation of SessionStore that puts data in the
> GAE cloud. The second option would not be difficult as you can see
> here:
>
> http://github.com/mmcgrana/ring/blob/master/ring-core/src/ring/middle...
>
> It is a very simple interface to implement.
>
> With either approach you could still use Sandbar if that is easier for
> you.
>
> Here is an example application that demonstrates using sessions in
> both ways.
>
> http://bit.ly/bKvBd7
>
> And here is another example that shows how to configure an alternative
> storage engine.
>
> http://github.com/brentonashworth/sandbar/blob/master/src/sandbar/exa...

James Reeves

unread,
Oct 6, 2010, 1:48:20 PM10/6/10
to ring-c...@googlegroups.com
That's one way of doing it, but it's not a way I'd recommend.

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

Reply all
Reply to author
Forward
0 new messages