creates, manages and refreshes jwt tokens
=> (require '[funcade.core :as f])
=> (def conf {:client-id "planet-earth"
:scope "solar-system"
:access-token-url "https://milky-way-galaxy/token.oauth2"
:client-secret "super-hexidecimal-secret"})
optional config properties:
name | default | description |
---|---|---|
:token-headers | {:Content-Type "application/x-www-form-urlencoded"} | headers passed to aquire the token |
:grant-type | "client_credentials" | oauth 2.0 grant type |
:refresh-percent | 10 | when less than % of time between expires-at and issued-at remains, refreshes the token |
=> (def token-repo (f/wake-token-master :serpens conf))
=> (f/current-token token-repo)
;; "eyJhbGci...dc22w"
user=> (f/stop token-repo)
true
=> (def sources {:mars {:client-id "..."
:client-secret "..."
:access-token-url "..."
:scope "..."}
:asgard {:client-id "..."
:client-secret "..."
:access-token-url "..."
:scope "..."}})
=> (def jwt (f/wake-token-masters sources))
creates two token masters:
=> jwt
;; {:mars #object[funcade.core.TokenMaster"],
;; :asgard #object[funcade.core.TokenMaster"]}
=> (-> jwt :mars f/current-token)
;; "eyJhbGci...dc22w"
=> (-> jwt :asgard f/current-token)
;; "eyJhbRkv...id95p"
=> (require '[funcade.jwks :as jk])
=> (jk/jwks->keys "https://foo.com/bar/jwks")
{"key-for-cert-one" #object[bouncycastle..BCRSAPublicKey "RSA Public Key [e7:ec:...]
"key-for-cert-two" #object[bouncycastle..BCRSAPublicKey "RSA Public Key [f1:25:...]
"key-for-cert-three" #object[bouncycastle..BCRSAPublicKey "RSA Public Key [b4:39:...]}
keys are looked up by token's kid:
=> (def keys (jk/jwks->keys "https://foo.com/bar/jwks"))
=> (def token "eyJhbGciOiJSUzI1Ni...")
#'user/token
=> (jk/find-token-key keys token)
#object[org.bouncycastle..BCRSAPublicKey "RSA Public Key [f1:25:]
funcade has middleware and helpers to use auth requests protected behind JWT tokens with various scopes.
=> (require '[reitit.ring :as ring])
=> (require '[funcade.middleware.reitit :as fun])
=> (def config {:jwk {:uri "https://milky-way-galaxy/ext/jwtsigningcert/jwks"})
=> (def app
(ring/ring-handler
(ring/router
["/ping" {:get {:scope :my-scope
:handler (fn [_]
{:status 200
:body "success"})}}]
{:data {:middleware [(fun/wrap-jwt-authentication config})
fun/scope-middleware]}})))
valid request:
=> (def token "eyJhbGci...dc22w")
=> (app {:request-method :get :uri "/ping" :headers {:authorization (str "Bearer " token)}})
;; {:status 200, :body "success"}
invalid/missing token:
=> (app {:request-method :get :uri "/ping"}})
;; {:status 401, :body {:error "invalid authorization header", :message "access to /ping is not authorized"}}
invalid scope:
=> (def token "eyJhbGci...dc22w")
=> (app {:request-method :get :uri "/ping" :headers {:authorization (str "Bearer " token)}})
;; {:status 401, :body {:message "missing required scope", :required :my-scope, :scopes (:not-my-scope)}}
import funcade.core.TokenMaster;
import tolitius.Funcade;
var config = Map.of("client-id", clientId,
"client-secret", clientSecret,
"access-token-url", accessTokenUri,
"username", username,
"password", password,
"grant-type", grantType);
var tokenMaster = Funcade.wakeTokenMaster("asgard", config);
and then whenever you need a token:
var token = Funcade.currentToken(tokenMaster);
copyright © 2022 @shvetsm / tolitius
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
Can you improve this documentation? These fine people already did:
anatoly, Mark Shvets, Anatoly & James KentEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close