108 lines
4.5 KiB
Clojure
108 lines
4.5 KiB
Clojure
(ns auto-ap.routes.auth
|
|
(:require
|
|
[auto-ap.datomic.users :as users]
|
|
[buddy.sign.jwt :as jwt]
|
|
[clj-http.client :as http]
|
|
[clj-time.core :as time]
|
|
[auto-ap.logging :as alog]
|
|
[config.core :refer [env]]
|
|
[com.brunobonacci.mulog :as mu]
|
|
[clojure.java.io :as io]
|
|
[clojure.edn :as edn]))
|
|
|
|
(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com")
|
|
(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g")
|
|
|
|
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
|
(defn make-api-token []
|
|
(jwt/sign {:user "API"
|
|
:exp (time/plus (time/now) (time/days 1000))
|
|
:user/role "admin"
|
|
:user/name "API"}
|
|
(:jwt-secret env)
|
|
{:alg :hs512}))
|
|
|
|
(defn gzip [data]
|
|
(let [data (pr-str data)
|
|
raw (java.io.ByteArrayOutputStream.)]
|
|
(with-open [output (-> raw
|
|
(io/output-stream)
|
|
(java.util.zip.GZIPOutputStream.))]
|
|
(io/copy data output))
|
|
(.encodeToString (java.util.Base64/getEncoder) (.toByteArray raw))))
|
|
|
|
(defn gunzip [b64]
|
|
|
|
(let [raw-bytes (.decode (java.util.Base64/getDecoder) b64)
|
|
raw (java.io.ByteArrayInputStream. raw-bytes)
|
|
out (java.io.ByteArrayOutputStream.)]
|
|
(with-open [compressed (-> raw
|
|
(io/input-stream)
|
|
(java.util.zip.GZIPInputStream.))]
|
|
(io/copy compressed out))
|
|
|
|
(edn/read-string (.toString out))))
|
|
|
|
(defn user->jwt [user oauth-token]
|
|
(let [auth (cond-> {:user (:user/name user)
|
|
:exp (time/plus (time/now) (time/days 30))
|
|
:db/id (:db/id user)
|
|
:user/role (name (:user/role user))
|
|
:user/name (:user/name user)}
|
|
(#{"admin" "read-only"} (name (:user/role user)))
|
|
(assoc :gz-clients (->> (:user/clients user)
|
|
(map (fn [c]
|
|
(select-keys c [:client/code :db/id :client/locations])))
|
|
|
|
gzip))
|
|
(not (#{"read-only" "admin"} (name (:user/role user))))
|
|
(assoc :user/clients
|
|
(->> (:user/clients user)
|
|
(map (fn [c]
|
|
(select-keys c [:client/code :db/id :client/locations]))))))]
|
|
(when (and user oauth-token)
|
|
auth)))
|
|
|
|
(defn oauth [{{:strs [code state]} :query-params {:strs [host]} :headers :as request}]
|
|
(try
|
|
(let [auth (-> "https://accounts.google.com/o/oauth2/token"
|
|
(http/post
|
|
{:form-params {"client_id" google-client-id
|
|
"client_secret" google-client-secret
|
|
"code" code
|
|
"redirect_uri" (str (:scheme env) "://" host "/api/oauth")
|
|
"grant_type" "authorization_code"}
|
|
:as :json})
|
|
:body)
|
|
|
|
token (:access_token auth)
|
|
profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo"
|
|
{:headers {"Authorization" (str "Bearer " token)} :as :json})
|
|
:body)
|
|
_ (mu/log ::got-profile
|
|
:profile profile)
|
|
user (users/find-or-insert! {:user/provider "google"
|
|
:user/provider-id (:id profile)
|
|
:user/email (:email profile)
|
|
:user/profile-image-url (:picture profile)
|
|
:user/name (:name profile)})]
|
|
;; TODO - these namespaces are not being transmitted/deserialized properly
|
|
|
|
(if-let [jwt (user->jwt user token)]
|
|
{:status 301
|
|
:headers {"Location" (str (or (not-empty state) "/") "?jwt="
|
|
(jwt/sign jwt
|
|
(:jwt-secret env)
|
|
{:alg :hs512}))}
|
|
:session {:identity (dissoc jwt :exp)}}
|
|
{:status 401
|
|
:body "Couldn't authenticate"}))
|
|
(catch Exception e
|
|
(alog/warn ::cant-authenticate
|
|
:error e)
|
|
{:status 401
|
|
:body (str "Couldn't authenticate " (.toString e))})))
|
|
|
|
(def routes {"api" {"/oauth" :oauth}})
|
|
(def match->handler {:oauth oauth})
|