In the last couple of weeks I have been trying to learn clojure. I startet to implement a simple web application so I have a playground where I can try things (It doesn’t really do anything at the moment, but I’ll write about it in a later post). I use the noir web framework to write this web application. In this web app I wanted to use OpenID, so I can log in with my google account.
I came across clj-openid by Steve Lindsay, but it still looks a kinda incomplete. I also found friend by Chas Emerick, but it seems to do everything which is too much for me. I decided to try openid4java, which I know from writing gclimbing.com. I know friend uses openid4java too, but I wanted to use it directly - at least for now.
I probably wrote some suboptimal - or even ugly - clojure code along the way. Please contact me if you have any suggestions what I could do better!
I first added openid4java-consumer 0.9.5 as a dependency to project.clj (0.9.6 does not have a jar on maven central, so I use 0.9.5 for now):
(defproject newsreader "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.4.0"]
[noir "1.3.0-beta3"]
[org.openid4java/openid4java-consumer "0.9.5"]]
:main newsreader.server)
Then I created two helper functions, one which starts the OpenID request (redirect->openid) and one to process the response (verify):
openid.clj
(ns newsreader.openid.openid
(:require [noir.request]
[noir.session :as session]))
(def ^:private consumerManager (org.openid4java.consumer.ConsumerManager.))
(defn redirect->openid [oidUrl returnUrl]
(let [discoveries (.discover consumerManager oidUrl)
discovered (.associate consumerManager discoveries)
authRequest (.authenticate consumerManager discovered returnUrl)]
(session/put! :newsreader-oid-discovered discovered)
(.getDestinationUrl authRequest true)))
(defn verify []
(let [request (noir.request/ring-request)
openidRequest (into {} (for [[k v] (:params request)] [(name k) v]))
responseParameters (org.openid4java.message.ParameterList. openidRequest)
discovered (session/get :newsreader-oid-discovered)
receivingUrl (str (name (:scheme request)) "://"
((:headers request) "host")
(:uri request)
"?" (:query-string request))
verification (.verify consumerManager receivingUrl responseParameters
discovered)
verified (.getVerifiedId verification)]
(session/remove! :newsreader-oid-discovered)
verified))
Using the code is quite simple: Redirect to the return value of redirect->openid (this is what the /login page does). Use verfiy in the OpenID response page (/openid-return). The result of verify is an Identifier - It should be used to uniquely identify the user.
login.clj
(ns newsreader.views.login
(:require [newsreader.views.common :as common]
[newsreader.openid.openid :as openid]
[noir.request]
[noir.session :as session])
(:use [noir.core :only [defpage]]
[hiccup.core :only [html]]))
(defpage "/login" []
(ring.util.response/redirect
(openid/redirect->openid "https://www.google.com/accounts/o8/id"
"http://localhost:8080/openid-return")))
(defpage "/openid-return" {openidns "openid.ns" openidmode "openid.mode"}
(str "now verified: " (openid/verify)))
Integrating openid4java was pretty easy. The resulting code (in openid.clj) does not look especially nice, but it’s only a single file with 2 functions so I can live with that. Maybe I’ll try friend in a later version, but for now I think I am fine with this code.