{"id":19302393,"url":"https://github.com/dryewo/fahrscheine-bitte","last_synced_at":"2025-04-22T10:33:48.171Z","repository":{"id":62432640,"uuid":"111295285","full_name":"dryewo/fahrscheine-bitte","owner":"dryewo","description":"Clojure library for checking OAuth2 access tokens","archived":false,"fork":false,"pushed_at":"2019-01-02T16:20:24.000Z","size":23,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-01T22:38:38.718Z","etag":null,"topics":["access-token","api","clojure","compojure","oauth2","security","swagger1st"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dryewo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-19T12:58:09.000Z","updated_at":"2020-02-17T12:27:43.000Z","dependencies_parsed_at":"2022-11-01T21:15:56.012Z","dependency_job_id":null,"html_url":"https://github.com/dryewo/fahrscheine-bitte","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Ffahrscheine-bitte","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Ffahrscheine-bitte/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Ffahrscheine-bitte/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Ffahrscheine-bitte/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dryewo","download_url":"https://codeload.github.com/dryewo/fahrscheine-bitte/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250221415,"owners_count":21394700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-token","api","clojure","compojure","oauth2","security","swagger1st"],"created_at":"2024-11-09T23:21:47.957Z","updated_at":"2025-04-22T10:33:47.532Z","avatar_url":"https://github.com/dryewo.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fahrscheine, bitte!\n\n[![Build Status](https://travis-ci.org/dryewo/fahrscheine-bitte.svg?branch=master)](https://travis-ci.org/dryewo/fahrscheine-bitte)\n[![codecov](https://codecov.io/gh/dryewo/fahrscheine-bitte/branch/master/graph/badge.svg)](https://codecov.io/gh/dryewo/fahrscheine-bitte)\n[![Clojars Project](https://img.shields.io/clojars/v/fahrscheine-bitte.svg)](https://clojars.org/fahrscheine-bitte)\n\nA Clojure library for verifying [OAuth2 access tokens].\nFor using in [Compojure] routes or in [Swagger1st] security handlers.\n\n\u003e \"Die Fahrscheine, bitte!\" is what *Fahrkartenkontrolleur* says when entering a bus. *Schwarzfahrer* are fearing this.\n\nAccess tokens are verified against [Introspection Endpoint]. In the examples in this document the address of\nthe endpoint is configured through `TOKENINFO_URL` environment variable.\n\nResponses of this endpoint are by default cached for 2 minutes and only for last 100 tokens (configurable).\nCreation of the cached token resolver function has to be done by the user using the provided helper function\nto enable testability and follow best practices: make state explicit. Please see examples below.\n\n## Usage\n\n```edn\n[fahrscheine-bitte \"0.4.0\"]\n```\n\nExamples assume the following:\n\n```clj\n(require '[fahrscheine-bitte.core :as oauth2]\n         '[mount.core :as m])\n\n(defn log-access-denied-reason [reason]\n  (log/info \"Access denied: %s\" reason))\n```\n\n1. Example with [mount] and [Swagger1st]:\n\n```clj\n(m/defstate oauth2-s1st-security-handler\n  :start (if-let [tokeninfo-url (System/getenv \"TOKENINFO_URL\")]\n           (let [access-token-resolver-fn (oauth2/make-cached-access-token-resolver tokeninfo-url {})]\n             (log/info \"Checking OAuth2 access tokens against %s.\" tokeninfo-url)\n             (oauth2/make-oauth2-s1st-security-handler access-token-resolver-fn oauth2/check-corresponding-attributes))\n           (do\n             (log/warn \"No TOKENINFO_URL set; NOT ENFORCING SECURITY!\")\n             (fn [request definition requirements]\n               request))))\n\n(m/defstate handler\n  :start (-\u003e (s1st/context :yaml-cp \"api.yaml\")\n             (s1st/discoverer)\n             (s1st/mapper)\n             (s1st/ring oauth2/wrap-reason-logger log-access-denied-reason)\n             (s1st/protector {\"oauth2\" oauth2-s1st-security-handler})\n             (s1st/parser)\n             (s1st/executor)))\n```\n\nIn this example we create a security handler that is given to `s1st/protector` to verify tokens on all endpoints that have\n`oauth2` security definition in place.\nAdditionally, we insert a middleware `wrap-log-auth-error` that will log all rejected access attempts.\n\n2. Example with [mount] and [Compojure]:\n\n```clj\n(m/defstate wrap-oauth2-token-verifier\n  :start (if-let [tokeninfo-url (System/getenv \"TOKENINFO_URL\")]\n           (let [access-token-resolver-fn (oauth2/make-cached-access-token-resolver tokeninfo-url {})]\n             (log/info \"Checking OAuth2 access tokens against %s.\" tokeninfo-url)\n             (oauth2/make-wrap-oauth2-token-verifier access-token-resolver-fn))\n           (do\n             (log/warn \"No TOKENINFO_URL set; NOT ENFORCING SECURITY!\")\n             identity)))\n\n(defn make-handler2 []\n  (-\u003e (routes\n        (GET \"/hello\" req {:status 200}))\n      (wrap-oauth2-token-verifier)\n      (oauth2/wrap-log-auth-error log-access-denied-reason)))\n```\n\n`(oauth2/make-wrap-oauth2-token-verifier access-token-resolver-fn)` returns a Ring middleware that can be used to\ncheck access tokens against given token introspection endpoint.\n\n### Configuring caching of tokeninfo responses\n\nYou can configure caching by passing the following parameters to `make-cached-access-token-resolver`:\n\n* `:ttl-ms` - number of milliseconds to keep cached results. Both positive and negative results are cached.\n  However, errored calls to tokeninfo are not cached. Default: 2000.\n* `:max-size` - number of results to cache. This has to be limited to avoid running out of memory. Default: 100\n\nExample:\n\n```clj\n(let [access-token-resolver-fn (oauth2/make-cached-access-token-resolver tokeninfo-url {:ttl-ms 5000 :max-size 1000})]\n  (oauth2/make-wrap-oauth2-token-verifier access-token-resolver-fn))\n```\n\n### Wrapping calls to tokeninfo endpoint\n\nFor tracing or logging reasons you might want to wrap the actual HTTP GET request to TOKENINFO_URL.\n\nThis is possible by giving `:client-middleware` parameter to `oauth2/make-cached-access-token-resolver`:\n\n```clj\n;; Define the middleware\n(defn wrap-client-tracing [http-get]\n  (fn [url params]\n    (println \"Calling tokeninfo URL:\" url)\n    (let [res (http-get url params)]\n      (println \"Tokeninfo result status:\" (:status res))\n      res)))\n\n;; Use the middleware when creating a token resolver\n(let [access-token-resolver-fn (oauth2/make-cached-access-token-resolver tokeninfo-url {:client-middleware wrap-client-tracing})]\n  (oauth2/make-wrap-oauth2-token-verifier access-token-resolver-fn))\n```\n\n`:client-middleware` is a middleware for a `clj-http.client/get`-like functions:\n\n```clj\n(ns clj-http.client)\n\n(defn get [url params]\n  ...)\n```\n\nClient middleware is a function that accepts a `clj-http.client/get`-like function —\nwhich takes 2 parameters, `url` and `params`, and returns a response map just like `clj-http.client/get` does —\nand returns another `clj-http.client/get`-like function.\n\nYou can read more in the [clj-http docs](https://github.com/dakrone/clj-http).\n\nBy default no client middleware is applied, `clj-http.client/get` is called directly.\n\n#### Customizing HTTP client call\n\nClient middleware can be used to adjust some parameters of `clj-http.client/get` call, for example, to set timeouts:\n\n```clj\n(defn wrap-client-timeouts [http-get]\n  (fn [url params]\n    ;; :conn-timeout and :socket-timeout do not overlap, giving possible duration of up to 2 seconds\n    (http-get url (merge params {:conn-timeout 1000 :socket-timeout 1000}))))\n\n;; Middlewares are combined using `comp`, first middleware is entered first\n(let [access-token-resolver-fn (oauth2/make-cached-access-token-resolver tokeninfo-url\n                                 {:client-middleware (comp wrap-client-tracing wrap-client-timeouts)})]\n  (oauth2/make-wrap-oauth2-token-verifier access-token-resolver-fn))\n\n``` \n\n**WARNING** It's not recommended to wrap the `http-get` call with `try`, as it will disrupt the logic of caching and error reporting.\n\n\n## License\n\nCopyright © 2019 Dmitrii Balakhonskii\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n\n[mount]: https://github.com/tolitius/mount\n[swagger1st]: https://github.com/zalando-stups/swagger1st\n[Compojure]: https://github.com/weavejester/compojure\n[Introspection Endpoint]: https://tools.ietf.org/html/rfc7662#section-2\n[OAuth2 access tokens]: https://tools.ietf.org/html/rfc6749#section-1.4\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryewo%2Ffahrscheine-bitte","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdryewo%2Ffahrscheine-bitte","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryewo%2Ffahrscheine-bitte/lists"}