Setting Up Google OAuth2 for Authentication With Joodo and Clojure
One of the first stories for the internal project that we’re working on is to integrate authorization for any registered users using Google’s OAuth2. I paired with Eric on this story on Wednesday and he was able to deftly port over much of what was done in the rails version of the app to set the authorization token. Then today Jeremy helped me troubleshoot the last few glitches we were having. Here’s a quick overview of the steps we used (and I’ll run this post past Eric and Jeremy to make sure I didn’t forget anything). Also, please note that this is just an initial run through and there will be a lot of refactoring and reorganizing before we’re done. You don’t want to do things like leave your Google keys in your controller- which we do below.
Including the Dependency
Thankfully, we found Pelle Braendgaard’s clauth library pretty quickly and much of his README helped guide us along. However, it was actually Suart Hinson’s version of clj-oauth2 that we ended up using, so the first step was to include it from Clojars.org in the dependencies section of the project.clj
file at the root level of our project. So now it looks something like this:
; project.clj
(defproject sample-app "0.0.1"
:description "A website deployable to AppEngine"
:dependencies [[org.clojure/clojure "1.4.0"]
[joodo "0.10.0"]
[speclj "2.2.0"]
[stuarth/clj-oauth2 "0.3.2"]]
:plugins [[speclj "2.2.0"]]
:test-paths ["spec/"]
:java-source-path "src/"
:repl-init-script "config/development/repl_init.clj"
:joodo-core-namespace sample-app.core)
Setting Up Google OAuth2 API Access
The next step, or so we thought, was to create an authentication controller that would direct the authentication request, send the appropriate information to Google and get the authorization token back. However, the “appropriate information” that you need comes from the Google API console once you’ve set up a project there. If you haven’t done that you can set up a project with them here. We had this set up but in case you don’t here’s the steps:
After setting up the project click on “API Access” over in the left hand panel and then the big “Create an OAuth 2.0 Client ID” button:
By default Google will provide you with a client ID, a client secret and automatically sets up https://localhost
as a redirect uri. You can add any path on the end of localhost you want, but because we have a Rails version of our app that uses a gem requiring a specific path, we used a matching path of “authentication/google-com-auth2”
TWO NOTES/GOTCHAS
1) If you don’t configure a specific port for the redirect-uri in the Google API console then you can use the path on multiple ports . Just set it to something like http://localhost/google-com-auth2 and then you could use localhost:3000/authentication/google-com-auth2 for a Rails App, localhost:9292/authentication/google-com-auth2 for a Sinatra App, or in our case, localhost:8080/authentication/com-google-auth2 for a Joodo app. You can also configure multiple redirect uri’s.
2) By default Google sets your redirect-uri to use https:
, however it’s a bit tricky to set up your local server to use SSL, so the easy solution for development is to just be sure your redirect-uri uses http:
And Time For the Controller
Now we were ready to set up our authentication-controller. Although it looks like there is a lot going on here, it’s actually pretty straightforward. Lets break it down section by section:
(ns sample-app.authentication.authentication-controller
(:require
[clj-oauth2.client :as oauth2]
[joodo.middleware.request :refer [*request*]]
[ring.util.response :refer [redirect]]
[compojure.core :refer (defroutes GET context)]))
In this first section at the top of our file we require clj-oauth2.client
at the top of our file as well as joodo.middleware.request
so that we can set the session with auth-token that Google sends back.
(def login-uri
(get (System/getenv) "LOGIN_URI" "https://accounts.google.com"))
In this block above we define the login-uri that sends our user over to the Google login page… which we use in the next block that contains all of the aforementioned appropriate information:
(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/google_oauth2/callback"
:client-id "*provided-to-you-in-google-api-console*"
:client-secret "*provided-to-you-in-google-api-console*"
:access-query-param :access_token
:scope ["https://www.googleapis.com/auth/userinfo.email"]
:grant-type "authorization_code"
:access-type "online"
:approval_prompt ""})
And in the final two sections we define the authorization request that uses the details from above, and then we define our two routes:
(def auth-req
(oauth2/make-auth-request google-com-oauth2))
(defroutes authentication-controller
(context "/authentication" []
(GET "/" [] (redirect (:uri auth-req)))
(GET "/google_oauth2/callback" []
(assoc
(redirect "/index")
:session
{:auth-token
{:value
(:access-token (oauth2/get-access-token google-com-oauth2 (:params *request*) auth-req))}}))))
I won’t lie, that final route includes enough parentheses to make my eyes cross, but what it’s doing is setting the auth-token value based on what comes back from the Google authorization, and then redirects the user to the index page.
Eventually we’re going to create an abstraction or two that properly checks these tokens and users, and we’ll move all of the Google OAuth2 details into another file as well. However, for now we are at least seeing the expected behavior in our tests and through the browser and we can at least check the that a session has an auth-token set.