-
-
Error: {{status}}
-
- {% if title %}
- {{title}}
- {% endif %}
- {% if message %}
- {{message}}
- {% endif %}
-
-
diff --git a/resources/html/home.html b/resources/html/home.html
index ba9e2d2..9643e97 100644
--- a/resources/html/home.html
+++ b/resources/html/home.html
@@ -26,7 +26,7 @@
{% if user %}
- Hello User!
+ Hello {{ user.username }}
{% else %}
Please login to use the graph
{% endif %}
@@ -63,12 +63,18 @@
diff --git a/src/clj/cat/moauth.clj b/src/clj/cat/moauth.clj
new file mode 100644
index 0000000..abb490a
--- /dev/null
+++ b/src/clj/cat/moauth.clj
@@ -0,0 +1,100 @@
+(ns cat.moauth
+ (:require [cat.config :refer [env]]
+ [clj-http.client :as httpclient]
+ [slingshot.slingshot :refer [try+]]
+ [clojure.tools.logging :as log]
+ [cat.layout :refer [error-page]]))
+
+; Inspired by https://leonid.shevtsov.me/post/oauth2-is-easy/
+
+(defn- oauth2-params []
+ {:client-id (env :oauth-consumer-key)
+ :client-secret (env :oauth-consumer-secret)
+ :authorize-uri (env :authorize-uri)
+ :redirect-uri (str (env :app-host) "/oauth/oauth-callback")
+ :access-token-uri (env :access-token-uri)
+ ;:scope "activity profile"
+ })
+
+; To authorize, redirect the user to the sign in / grant page
+
+(defn- authorize-uri
+ [client-params ;csrf-token
+ ]
+ (str
+ (:authorize-uri client-params)
+ "?"
+ (httpclient/generate-query-string {:response_type "code"
+ :client_id (:client-id client-params)
+ :redirect_uri (:redirect-uri client-params)})
+ ;"response_type=code"
+ ;"&client_id="
+ ;(url-encode (:client-id client-params))
+ ;"&redirect_uri="
+ ;(url-encode (:redirect-uri client-params))
+ ;"&scope="
+ ;(url-encode (:scope client-params))
+ ;"&state="
+ ;(url-encode csrf-token)
+ ))
+
+(defn authorize-api-uri
+ "let the user authorize access by redirecting to the signin / grant page
+ of the used oauth api"
+ []
+ (authorize-uri (oauth2-params)))
+
+(defn get-authentication-response
+ "Request an access token with the obtained unique code from the grant page"
+ [csrf-token {:keys [state code]}]
+ (if (or true (= csrf-token state))
+ (try
+ (do
+ (log/info "Requesting access token with code " code)
+ (let [oauth2-params (oauth2-params)
+ access-token (httpclient/post (:access-token-uri oauth2-params)
+ {:form-params {:code code
+ :grant_type "authorization_code"
+ :client_id (:client-id oauth2-params)
+ :client_secret (:client-secret oauth2-params)
+ :redirect_uri (:redirect-uri oauth2-params)}
+ ;:basic-auth [(:client-id oauth2-params) (:client-secret oauth2-params)]
+ :as :json
+ })]
+ (println "Access token response:" access-token)
+ (:body access-token)))
+ (catch Exception e (log/error "Something terrible happened..." e)))
+ nil))
+
+(defn get-user-info
+ "User info API call"
+ [access-token]
+ (let [url (str (env :user-api-uri))]
+ (-> (httpclient/get url {:oauth-token access-token
+ :as :json})
+ :body)
+ ))
+
+; Refresh token when it expires
+(defn- refresh-tokens
+ "Request a new token pair"
+ [refresh-token]
+ (try+
+ (let [oauth2-params (oauth2-params)
+ {{access-token :access_token refresh-token :refresh_token} :body}
+ (httpclient/post (:access-token-uri oauth2-params)
+ {:form-params {:grant_type "refresh_token"
+ :refresh_token refresh-token}
+ :basic-auth [(:client-id oauth2-params) (:client-secret oauth2-params)]
+ :as :json})]
+ [access-token refresh-token])
+ (catch [:status 401] _ nil)))
+
+(defn get-fresh-tokens
+ "Returns current token pair if they have not expired, or a refreshed token pair otherwise"
+ [access-token refresh-token]
+ (try+
+ (and (get-user-info access-token)
+ [access-token refresh-token])
+ (catch [:status 401] _ (refresh-tokens refresh-token))))
+
diff --git a/src/clj/cat/oauth.clj b/src/clj/cat/oauth.clj
index bfb67d3..7d1c06f 100644
--- a/src/clj/cat/oauth.clj
+++ b/src/clj/cat/oauth.clj
@@ -16,14 +16,17 @@
(defn oauth-callback-uri
"Generates the oauth request callback URI"
[{:keys [headers]}]
- (str (headers "x-forwarded-proto") "://" (headers "host") "/oauth/oauth-callback"))
+ (let [callback-url (str "http://" (headers "host") "/oauth/oauth-callback")]
+ (println "Generated callback url:" callback-url)
+ callback-url))
(defn fetch-request-token
"Fetches a request token."
[request]
(let [callback-uri (oauth-callback-uri request)]
(log/info "Fetching request token using callback-uri" callback-uri)
- (oauth/request-token consumer (oauth-callback-uri request))))
+ (log/info "Oauth consumer: " consumer)
+ (oauth/request-token consumer callback-uri {:grant_type "authorization_code"})))
(defn fetch-access-token
[request_token]
@@ -31,5 +34,9 @@
(defn auth-redirect-uri
"Gets the URI the user should be redirected to when authenticating."
- [request-token]
- (str (oauth/user-approval-uri consumer request-token)))
+ ([request]
+ (auth-redirect-uri request ""))
+ ([request request-token]
+ (str (oauth/user-approval-uri consumer request-token {:response_type "code"
+ :client_id (env :oauth-consumer-key)
+ :redirect_uri (oauth-callback-uri request)}))))
\ No newline at end of file
diff --git a/src/clj/cat/routes/home.clj b/src/clj/cat/routes/home.clj
index 8d435bc..3bbc88f 100644
--- a/src/clj/cat/routes/home.clj
+++ b/src/clj/cat/routes/home.clj
@@ -7,7 +7,8 @@
[struct.core :as st]
[clojure.edn :as edn]
[clojure.tools.logging :as log]
- [clojure.data.json :as json]))
+ [clojure.data.json :as json]
+ [oauth.client :as oauth]))
(def user-schema
[[:name st/required st/string]
@@ -29,10 +30,11 @@
(db/get-users))
(defroutes home-routes
- (GET "/" []
+ (GET "/" req
(let [users (get-users)
relations (get-relations)]
- (home-page {:relations relations :users users :user {}})))
+ (log/info (str "Session: " (:session req)))
+ (home-page {:relations relations :users users :user (get-in req [:session :user])})))
;(GET "/docs" []
; (-> (response/ok (-> "docs/docs.md" io/resource slurp))
; (response/header "Content-Type" "text/plain; charset=utf-8")))
diff --git a/src/clj/cat/routes/oauth.clj b/src/clj/cat/routes/oauth.clj
index 7c98d0b..fe483be 100644
--- a/src/clj/cat/routes/oauth.clj
+++ b/src/clj/cat/routes/oauth.clj
@@ -3,32 +3,49 @@
[compojure.core :refer [defroutes GET]]
[clojure.java.io :as io]
[cat.oauth :as oauth]
- [clojure.tools.logging :as log]))
+ [clojure.tools.logging :as log]
+ [cat.moauth :as mo]))
+
+(defn set-user! [user session redirect-url]
+ (-> (found redirect-url)
+ (assoc :session (assoc session :user user))))
+
+(defn remove-user! [session redirect-url]
+ (-> (found redirect-url)
+ (assoc :session (dissoc session :user))))
+
+(defn clear-session! [redirect-url]
+ (-> (found redirect-url)
+ (dissoc :session)))
(defn oauth-init
"Initiates the Twitter OAuth"
[request]
- (-> (oauth/fetch-request-token request)
- :oauth_token
- oauth/auth-redirect-uri
+ (-> (mo/authorize-api-uri)
found))
(defn oauth-callback
- "Handles the callback from Twitter."
- [{:keys [session params]}]
+ "Handles the callback from adams."
+ [req_token {:keys [params session]}]
; oauth request was denied by user
(if (:denied params)
(-> (found "/")
(assoc :flash {:denied true}))
; fetch the request token and do anything else you wanna do if not denied.
- (let [{:keys [user_id screen_name]} (oauth/fetch-access-token params)]
- (log/info "successfully authenticated as" user_id screen_name)
- (-> (found "/")
- (assoc :session
- (assoc session :user-id user_id :screen-name screen_name))))))
+ (let [{:keys [access_token refresh_token]} (mo/get-authentication-response nil req_token)]
+ (log/info "Successfully fetched access-id: " access_token)
+ (log/info "Fetching user info")
+ (let [user (mo/get-user-info access_token)]
+ (log/info "User info: " user)
+ (set-user! user session "/")))))
+
+;(catch [:status 401] _
+; (error-page {:status 401
+; :title "Error authenticating"
+; :message "Please contact your system administrator to fix this issue"}))
(defroutes oauth-routes
- (GET "/oauth/oauth-init" req (oauth-init req))
- (GET "/oauth/oauth-callback" [& req_token :as req] (oauth-callback req)))
-
+ (GET "/oauth/oauth-init" req (oauth-init req))
+ (GET "/oauth/oauth-callback" [& req_token :as req] (oauth-callback req_token req))
+ (GET "/logout" req (remove-user! (:session req) "/")))
\ No newline at end of file