Create a gist now

Instantly share code, notes, and snippets.

JWT Parsing in Dunaj
(ns foo.core
(:api dunaj)
(:require [dunaj.host.int :refer [i== iDOT]]
[dunaj.host.array :as dha]
[dunaj.format.base64 :refer [base64-safe]]
[dunaj.coll.recipe :refer [concat*]]
[dunaj.concurrent.port :refer [reduce! onto-chan!]]))
(def+ ByteColl [java.lang.Byte]) ;; type signature
(defn split-token :- KeywordMap
"Parses JWT token and returns map with its parts."
[token :- ByteColl]
(let [parts (vec (partition-by #(i== (iDOT) %) token))
kjson (assoc json :key-decode-fn keyword)
pf #(parse-whole kjson (parse utf-8 (parse base64-safe %)))]
{:signed-part (concat* (take 3 parts))
:header (pf (first parts))
:claims (pf (nth parts 2))
:signature (parse base64-safe (nth parts 4 nil))}))
(defn secret-sign :- ByteColl
"Returns the result of signing data with secret."
[alg :- String, secret :- ByteColl, data :- ByteColl]
(let [secret-key (javax.crypto.spec.SecretKeySpec. (dha/byte-array secret) alg)
hmac (javax.crypto.Mac/getInstance alg)]
(.init hmac secret-key)
(dha/adapt (.doFinal hmac (dha/byte-array data)))))
(defn jwt :- ByteColl
[claims :- {}, secret :- ByteColl]
(let [pf #(print base64-safe (print utf-8 (print-one json %)))
header {:typ "JWT" :alg "HS256"}
signed-part (concat (pf header) [(iDOT)] (pf claims))
signature (secret-sign "HmacSHA256" secret signed-part)]
(vec (concat signed-part [(iDOT)] (print base64-safe signature)))))
(def my-token "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ")
(def my-secret "secret")
;;;; Scratch
(scratch []
;; Parsing
(def token-bytes (print utf-8 my-token))
(def parsed-token (split-token token-bytes))
parsed-token
;; {:signed-part #object[dunaj.coll.helper.Reduciblecbus 0x73002b3d "dunaj.coll.helper.Reduciblecbus@73002b3d"],
;; :header {:alg "HS256", :typ "JWT"},
;; :claims {:sub "1234567890", :name "John Doe", :admin true},
;; :signature #object[dunaj.coll.helper.Reduciblecbus 0x28a24be3 "dunaj.coll.helper.Reduciblecbus@28a24be3"]
;; }
(:claims parsed-token)
;; {:sub "1234567890", :name "John Doe", :admin true}
;; Verification
(vec (:signature parsed-token))
;; [76 -107 64 -9 -109 -85 51 -79 54 112 22 -101 -33 68 76 30 -79 -61 112 71 -15 -114 -122 25 -127 -31 78 52 88 123 30 4]
(def secret-bytes (print utf-8 my-secret))
(secret-sign "HmacSHA256" secret-bytes (:signed-part parsed-token))
;; (76 -107 64 -9 -109 -85 51 -79 54 112 22 -101 -33 68 76 30 -79 -61 112 71 -15 -114 -122 25 -127 -31 78 52 88 123 30 4)
(= (seq (vec (:signature parsed-token)))
(seq (secret-sign "HmacSHA256" secret-bytes (:signed-part parsed-token))))
;; true
;; Token creation
(def new-token-bytes (jwt {:sub "1234567890", :name "John Doe", :admin false} secret-bytes))
(def new-token (str (parse utf-8 new-token-bytes)))
;; eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlfQ==.S_mcs280KaxVTd5I3-kPeSwyrtOXZJHqfPF5RH2gQV0=
;; Example on using transducers
(let [xf (comp (parse base64-safe)
(parse utf-8)
(parse json))
c (chan 100 xf)
v (vec (print utf-8 "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9"))]
(<!! (onto-chan! c v))
(thread (pp! (<!! (reduce! conj [] c))))
(close! c))
;; [{"typ" "JWT", "alg" "HS256"} {"sub" "1234567890", "name" "John Doe", "admin" true}]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment