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.