Recently in my Clojure project we had to integrate with Google’s OAuth for authenticating our users. It was a pretty painful process, so here’s a quick guide for anyone that wants to do this in the future.
For this guide I will be using stuarth/clj-oauth2 "0.3.2"
. First add that as
a dependency to your project.clj file. Next lets create a authentication
module.
(ns authentication
(:require
[cheshire.core :refer [parse-string]
[clj-oauth2.client :as oauth2]))
(def login-uri
"https://accounts.google.com")
(def google-com-oauth2
{:authorization-uri (str login-uri "/o/oauth2/auth")
:access-token-uri (str login-uri "/o/oauth2/token")
:redirect-uri "http://localhost:8080/authentication/callback"
:client-id "CLIENT"
:client-secret "CLIENT-SECRET"
:access-query-param :access_token
:scope ["https://www.googleapis.com/auth/userinfo.email"]
:grant-type "authorization_code"
:access-type "online"
:approval_prompt ""})
(def auth-req
(oauth2/make-auth-request google-com-oauth2))
(defn- google-access-token [request]
(oauth2/get-access-token google-com-oauth2 (:params request) auth-req))
(defn- google-user-email [access-token]
(let [response (oauth2/get "https://www.googleapis.com/oauth2/v1/userinfo" {:oauth access-token})]
(get (parse-string (:body response)) "email")))
;; Redirect them to (:uri auth-req)
;; When they comeback to /authentication/callback
(google-user-email ;=> user's email trying to lgo in
(google-access-token *request*))
So what did we do here? First of all we required the OAuth2 dependency into
our namespace. We also included cheshire, Clojure’s JSON parsing library. Then we
created a hash google-com-oauth2
. This hash contains all of the information Google
needs when we request a OAuth2 access token. Replace the :client-id and
:client-secret with the values you get from Google when you set up your Google
application. Also be sure that your :redirect-uri matches the one you supplied
Google.
Using this data has we can construct a auth-req using our OAuth2 library. When
users go to our application, when they try and log on the app should redirect
them to (:uri authentication/auth-req)
.
When the user gets back to our application it will be at out callback uri. The request params of this request should look like,
{:code "4/dasfjkhadsfkalsdasdfaskjf}
Using this request object we can get back a access-token from Google. Finally once we have an access token, we get start making oauth/get’s to retrieve user info from Google. I’ve written the method google-user-email, but you can get other values from the user if you change the scope of your request.