forked from yogsototh/clj-jwt
-
Notifications
You must be signed in to change notification settings - Fork 3
/
core.clj
108 lines (89 loc) · 3.76 KB
/
core.clj
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
(ns clj-jwt.core
(:require
[clj-jwt.base64 :refer [url-safe-encode-str url-safe-decode-str]]
[clj-jwt.sign :refer [get-signature-fn get-verify-fn supported-algorithm?]]
[clj-jwt.intdate :refer [ensure-intdate]]
[clj-jwt.json-key-fn :refer [write-key read-key]]
[clojure.string :as str]
[jsonista.core :as jsonista]))
(def ^:private DEFAULT_SIGNATURE_ALGORITHM :HS256)
(def ^:private DEFAULT_KID nil)
(def ^:private jsonista-mapper
(jsonista/object-mapper {:encode-key-fn write-key
:decode-key-fn read-key}))
(def ^:private map->encoded-json (comp url-safe-encode-str
(fn [m]
(jsonista/write-value-as-string m jsonista-mapper))))
(def ^:private encoded-json->map (comp (fn [^String s]
(jsonista/read-value s jsonista-mapper))
url-safe-decode-str))
(defn- update-map [m k f] (if (contains? m k) (update-in m [k] f) m))
(defrecord JWT [header claims signature encoded-data])
; ----------------------------------
; JsonWebToken
; ----------------------------------
(defprotocol JsonWebToken
"Protocol for JsonWebToken"
(init [this claims] "Initialize token")
(encoded-header [this] "Get url-safe base64 encoded header json")
(encoded-claims [this] "Get url-safe base64 encoded claims json")
(to-str [this] "Generate JsonWebToken as string"))
(extend-protocol JsonWebToken
JWT
(init [this claims]
(let [claims (reduce #(update-map % %2 ensure-intdate) claims [:exp :nbf :iat])]
(assoc this :header {:alg "none" :typ "JWT"} :claims claims :signature "")))
(encoded-header [this]
(-> this :header map->encoded-json))
(encoded-claims [this]
(-> this :claims map->encoded-json))
(to-str [this]
(str (encoded-header this) "." (encoded-claims this) "." (get this :signature ""))))
; ----------------------------------
; JsonWebSignature
; ----------------------------------
(defprotocol JsonWebSignature
"Protocol for JonWebSignature"
(set-alg [this alg] "Set algorithm name to JWS Header Parameter")
(set-kid [this kid] "Set Key ID to JWS Header Parameter")
(sign [this key] [this alg key] [this alg key kid] "Set signature to this token")
(verify [this] [this key] [this algorithm key] "Verify this token"))
(extend-protocol JsonWebSignature
JWT
(set-alg [this alg]
(assoc-in this [:header :alg] (name alg)))
(set-kid [this kid]
(assoc-in this [:header :kid] kid))
(sign
([this key] (sign this DEFAULT_SIGNATURE_ALGORITHM key DEFAULT_KID))
([this alg key] (sign this alg key DEFAULT_KID))
([this alg key kid]
(let [this* (cond-> (set-alg this alg)
(some? kid) (set-kid kid))
sign-fn (get-signature-fn alg)
data (str (encoded-header this*) "." (encoded-claims this*))]
(assoc this* :signature (sign-fn key data) :encoded-data data))))
(verify
([this] (verify this ""))
([this key]
(let [alg (-> this :header :alg keyword)]
(cond
(= :none alg) (= "" key (:signature this))
(supported-algorithm? alg)
(let [verify-fn (get-verify-fn alg)]
(verify-fn key (:encoded-data this) (:signature this)))
:else (throw (Exception. "Unkown signature")))))
([this algorithm key]
(if (= algorithm (-> this :header :alg keyword))
(verify this key)
false))))
; =jwt
(defn jwt [claim] (init (->JWT "" "" "" "") claim))
; =str->jwt
(defn str->jwt
[jwt-string]
(let [[header claims signature] (str/split jwt-string #"\.")]
(->JWT (encoded-json->map header)
(encoded-json->map claims)
(or signature "")
(str header "." claims))))