(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) }) ; 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" [] (log/info "Oauth params: " (oauth2-params)) (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))))