Monday, April 30, 2018

Google Sign-In using Clojure

Last time I wrote an article for providing Facebook Sign-In using Clojure. This article will guide you to add Google Sign-In functionality in your web application using clojure. We’ll use compojure.core for routing, clj-http.client for http requests, cheshire.core for parsing json and noir.response for redirections.


First, make a new google project to get your project’s Client ID and Client Secret Key from this URL: https://console.developers.google.com/project.

Now define a new namespace as:
1
2
3
4
5
(ns exampleapp.routes.google
 (:use compojure.core)
 (:require [clj-http.client :as client]
           [cheshire.core :as parse]
           [noir.response :as resp]))

In “google” namespace, define following variables:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(def CLIENT_ID "your project's client id")
(def REDIRECT_URI "http://localhost:3000/auth_google")
(def login-uri "https://accounts.google.com")
(def CLIENT_SECRET "your project's client secret key")
(def google-user (atom {:google-id "" :google-name "" :google-email ""}))
 
(def red (str "https://accounts.google.com/o/oauth2/auth?"
              "scope=email%20profile&"
              "redirect_uri=" (ring.util.codec/url-encode REDIRECT_URI) "&"
              "response_type=code&"
              "client_id=" (ring.util.codec/url-encode CLIENT_ID) "&"
              "approval_prompt=force"))

“red” defines the request url on which the user is redirected to ask for user permission for user details. We use ring.util.codec/url-encode to encode the url details. ring.util.codec/url-encode uses UTF-8 encoding by default.
Define routes in “google” namespace in order to redirect a user to the url defined by “red” as follows:

1
2
(defroutes google-routes
 (GET "/google" [] (resp/redirect red)))

After user provides permission to your google project, you will get an authorization code on the the redirect uri that you mentioned in your google project, the same that you defined in your namespace, i.e http://localhost:3000/auth_google.
Now define a new route in google-routes as:

1
2
(GET "/auth_google" {params :query-params}
 (google params))

and a new function in order to get the access code and user details as:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(defn google [params]
 (let [access-token-response (client/post "https://accounts.google.com/o/oauth2/token"
                                          {:form-params {:code (get params "code")
                                                         :client_id CLIENT_ID
                                                         :client_secret CLIENT_SECRET
                                                         :redirect_uri REDIRECT_URI
                                                         :grant_type "authorization_code"}})
       user-details (parse/parse-string (:body (client/get (str "https://www.googleapis.com/oauth2/v1/userinfo?access_token="
 (get (parse/parse-string (:body access-token-response)) "access_token")))))]
 (swap! google-user #(assoc % :google-id %2 :google-name %3 :google-email %4) (get user-details "id") (get user-details "name") (get user-details "email"))))

In the above function we use clj-http’s “post” to make a post request to get access token followed by “get” to get user information. Then we use cheshire.core’s “parse-string” to parse json response and convert it into clojure object and save the information in the atom defined as “google-user”.
Entire “google” namespace is as follows:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(ns exampleapp.routes.google
 (:use compojure.core)
 (:require [clj-http.client :as client]
           [cheshire.core :as parse]
           [noir.response :as resp]))
 
(def CLIENT_ID "your project's client id")
(def REDIRECT_URI "http://localhost:3000/auth_google")
(def login-uri "https://accounts.google.com")
(def CLIENT_SECRET "your project's client secret key")
(def google-user (atom {:google-id "" :google-name "" :google-email ""}))
 
(def red (str "https://accounts.google.com/o/oauth2/auth?"
              "scope=email%20profile&"
              "redirect_uri=" (ring.util.codec/url-encode REDIRECT_URI) "&"
              "response_type=code&"
              "client_id=" (ring.util.codec/url-encode CLIENT_ID) "&"
              "approval_prompt=force"))
 
(defn google [params]
 (let [access-token-response (client/post "https://accounts.google.com/o/oauth2/token"
                                          {:form-params {:code (get params "code")
                                           :client_id CLIENT_ID
                                           :client_secret CLIENT_SECRET
                                           :redirect_uri REDIRECT_URI
                                           :grant_type "authorization_code"}})
       user-details (parse/parse-string (:body (client/get (str "https://www.googleapis.com/oauth2/v1/userinfo?access_token="
 (get (parse/parse-string (:body access-token-response)) "access_token")))))]
 (swap! google-user #(assoc % :google-id %2 :google-name %3 :google-email %4) (get user-details "id") (get user-details "name") (get user-details "email"))))
 
(defroutes google-routes
 (GET "/auth_google" {params :query-params}
 (google params))
 
(GET "/google" [] (resp/redirect red)))

This article was first published on the Knoldus blog. 

Facebook Sign-in using Clojure

In order to provide Facebook sign-in functionality you need to create a new Facebook App from this url: https://developers.facebook.com/docs/facebook-login. Provide the redirection URI that you’ll use for the App. After setting up the app you’ll get an App ID and App Secret key.



We’ll use “clj-oauth2.client” to redirect a user to the authentication page, “clj-http.client” for HTTP requests, “cheshire.core” to parse Json string in order to get keywords, “noir.respose” to provide re-directions and “compojure.core” for routing.
We’ll begin with defining a new namespace:
1
2
3
4
5
6
(ns exampleapp.routes.facebook
 (:use compojure.core)
 (:require [clj-oauth2.client :as oauth2]
           [noir.response :as resp]
           [clj-http.client :as client]
           [cheshire.core :as parse]))

In facebook namespace define an atom, facebook-user, to store Facebook user details:
1
2
(def facebook-user
  (atom {:facebook-id "" :facebook-name "" :facebook-email ""}))

Now include the App ID and App Secret key you just got after creating a new Facebook app along with the redirection URI, in the code below:
1
2
3
(def APP_ID "your app id")
(def APP_SECRET "your app secret key")
(def REDIRECT_URI "http://localhost:3000/auth_facebook")

and define an oauth2 map containing all the details required for Facebook log in:
1
2
3
4
5
6
7
8
9
(def facebook-oauth2
 {:authorization-uri "https://graph.facebook.com/oauth/authorize"
  :access-token-uri "https://graph.facebook.com/oauth/access_token"
  :redirect-uri REDIRECT_URI
  :client-id APP_ID
  :client-secret APP_SECRET
  :access-query-param :access_token
  :scope ["email"]
  :grant-type "authorization_code"})

We can use “make-auth-request” function defined in “clj-oauth2.client” library to get the redirect URI on which the user should be redirected. The user can now give access to the app we created in first step.

1
2
(resp/redirect
  (:uri (oauth2/make-auth-request facebook-oauth2)))

Note that we used noir.response/redirect function to provide the redirection.
After a Facebook user grant access to the app, we’ll get a response containing access token on the redirection URI we provided while setting up our Facebook app. We’ll use “compojure.core’s” GET to get the response:

1
2
(defroutes facebook-routes
 (GET "/auth_facebook" {params :query-params} (facebook params)))

and pass it to a function in order to get the access token and user details.
We’ll use the code below for getting access token and user details:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(defn facebook [params]
 (let [access-token-response (:body (client/get (str "https://graph.facebook.com/oauth/access_token?"
                                                     "client_id=" APP_ID
                                                     "&redirect_uri=" REDIRECT_URI
                                                     "&client_secret=" APP_SECRET
                                                     "&code=" (get params "code"))))
       access-token (get (re-find #"access_token=(.*?)&expires=" access-token-response) 1)
       user-details (-> (client/get (str "https://graph.facebook.com/me?access_token=" access-token))
                        :body
                        (parse/parse-string))]
  (swap! facebook-user
   #(assoc % :facebook-id %2 :facebook-name %3 :facebook-email %4)
     (get user-details "id")
     (get user-details "first_name")
     (get user-details "email"))))

This article was first published on the Knoldus blog.