Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
clojurescript JWT encode/decode (SHA version only)
(ns cljsjs.jwt
[clojure.spec :as s]
[clojure.string :as str]
[goog.json :as json]
[goog.crypt.base64 :refer [encodeString decodeString]]))
;; goog.crypt.hmac produces different signature than nodejs version
(def jssha (js/require "jssha"))
(def signing-algorithm-map
{"HS256" "sha256"
"HS384" "sha384"
"HS512" "sha512"
"RS256" "RSA-SHA256"})
(def signing-type-map
{"HS256" "hmac"
"HS384" "hmac"
"HS512" "hmac"
"RS256" "sign"})
(defn- base64-url-escape
(-> b64string
(str/replace "+" "-")
(str/replace "/" "_")
(str/replace "=" "")))
(defn- base64-url-encode [string] (base64-url-escape (encodeString string)))
(defn- create-hmac
[algo key]
(condp = algo
"sha256" (jssha. "SHA-256" "TEXT")
"sha384" (jssha. "SHA-384" "TEXT")
"sha512" (jssha. "SHA-512" "TEXT")
:else (throw (js/Error. (str "unknown hmac hasher `" algo "`")))))
(defn- sign
[input key algo type]
(condp = type
(let [hmac (create-hmac algo type)]
(.setHMACKey hmac key "TEXT")
(.update hmac input)
(base64-url-escape (.getHMAC hmac "B64")))
:else (throw (js/Error. "algorithm not supported"))))
(defn- verify-sig
[input sig key method type]
(condp = type
"hmac" (= sig (sign input key method type))
:else (throw (js/Error. "algorithm not supported"))))
(defn ^:export decode
"decode from JWT"
[token key & [no-verify algo]]
(let [segments (s/conform (s/cat :header string? :payload string? :signature string?)
(str/split token "."))]
(if-not (map? segments)
(throw (js/Error. "invalid token"))
(let [header (json/parse (decodeString (:header segments)))
payload (json/parse (decodeString (:payload segments)))
no-verify (if (nil? no-verify) false)]
(if no-verify
(let [now (.. js/Date (now))
typ (.. header -typ)
alg (.. header -alg)
nbf (.. header -nbf)
exp (.. header -exp)
signing-method (get signing-algorithm-map (or algo alg))
signing-type (get signing-type-map (or algo alg))]
(if-not (= "JWT" typ) (throw (js/Error. "not valid jwt token typ")))
(if-not (and signing-method signing-type) (throw (js/Error. "algorithm not supported")))
(if (< now (* 1000 nbf)) (throw (js/Error. "token not yet active")))
(if (> now (* 1000 nbf)) (throw (js/Error. "token already expired")))
(if-not (verify-sig (str (:header segments) "." (:payload segments))
(:signature segments)
key signing-method signing-type)
(throw (js/Error. "signature verification failed")))
(defn ^:export encode
"encode to JWT"
[payload key & [algo extra-headers]]
(let [algo (or algo "HS256")
extra-headers (or extra-headers {})
signing-method (get signing-algorithm-map algo)
signing-type (get signing-type-map algo)]
(if-not (map? payload)
(throw (js/Error. "payload should be in JSON format")))
(if-not (map? extra-headers)
(throw (js/Error. "extra-headers should be a map")))
(if-not (and signing-method signing-type)
(throw (js/Error. "algorithm not supported")))
(let [header (base64-url-encode (json/serialize (clj->js (apply conj extra-headers {:alg algo :typ "JWT"}))))
payload (base64-url-encode (json/serialize (clj->js payload)))
signature (sign (str header "." payload) key signing-method signing-type)]
(str header "." payload "." signature))))
Copy link

deepaksingh007 commented Jul 17, 2019


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment