{"id":27165779,"url":"https://github.com/ivarref/tamperproof-uri-state","last_synced_at":"2025-04-10T03:49:15.214Z","repository":{"id":286576916,"uuid":"961383332","full_name":"ivarref/tamperproof-uri-state","owner":"ivarref","description":"Tamperproof uri state. With expiration.","archived":false,"fork":false,"pushed_at":"2025-04-07T09:21:02.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-07T09:34:59.719Z","etag":null,"topics":["clojure","hash","hmac-sha256","signing","tamperproof","unsigning","uri"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivarref.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-04-06T12:04:17.000Z","updated_at":"2025-04-07T09:21:06.000Z","dependencies_parsed_at":"2025-04-07T09:45:06.447Z","dependency_job_id":null,"html_url":"https://github.com/ivarref/tamperproof-uri-state","commit_stats":null,"previous_names":["ivarref/tamperproof-uri-state"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Ftamperproof-uri-state","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Ftamperproof-uri-state/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Ftamperproof-uri-state/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Ftamperproof-uri-state/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivarref","download_url":"https://codeload.github.com/ivarref/tamperproof-uri-state/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247969032,"owners_count":21025903,"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":["clojure","hash","hmac-sha256","signing","tamperproof","unsigning","uri"],"created_at":"2025-04-09T03:20:50.537Z","updated_at":"2025-04-09T03:20:51.389Z","avatar_url":"https://github.com/ivarref.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tamperproof-uri-state\n\nTamperproof uri state. With expiration.\n\nEver wanted to store some tamperproof state in a URL? Look no further.\n\nZero external dependencies (besides the JVM).\n\n## Rationale\n\nYour service sends an end user to an external service for a short task.\nThe external service supports custom callback URLs / URL parameters.\n\nYour service does not want to\nstore unnecessary state, so it includes the state it needs to resume\nwork after the task is completed in the callback URL.\n\nAs the state is kept by the external service, a restart to your service will\nnot delete the state.\n\nWhen your service is visited with the callback URL, it can verify that the callback\nstate is signed by your service and then use the state to resume doing whatever it\nneeds to do.\n\nFor example in a signing process:\n\n```mermaid\nsequenceDiagram\n    enduser-\u003e\u003eyourservice: Login for the first time\n    yourservice-\u003e\u003esignservice: Create signature job. Set callback url state to (tus/sign \"my-key\" (tus/plus-now 3600) user-id)\n    yourservice-\u003e\u003eenduser: Redirect to signservice \n    enduser-\u003e\u003esignservice: Signs the document\n    signservice-\u003e\u003eenduser: Redirects back to yourservice with URI state included\n    enduser-\u003e\u003eyourservice: Visit callback URL with state\n    yourservice-\u003e\u003eyourservice: Verifies and extracts state (user-id) \n    yourservice-\u003e\u003epersistentDB: Marks that user-id has signed the PDF\n    yourservice-\u003e\u003eenduser: Present welcome page\n```\n\n## Installation\n\n```\ncom.github.ivarref/tamperproof-uri-state {:git/sha \"448b84a5d8ae79055b385d08ad568a4f55435b47\"}\n```\n\n## Usage\n\n```clojure\n(require '[com.github.ivarref.tamperproof-uri-state :as tus])\n\n(tus/sign \"my-key\" (tus/plus-now 3600) \"my-state\")\n; Valid for one hour (3600 seconds): (+ 3600 (/ (System/currentTimeMillis) 1000))\n=\u003e \"bXktc3RhdGU=.Z_KAig==.s6VGTnzNyzStOoJpGxtuDVvNwqh6HDfrWId3adn6Q98=\"\n\n(tus/unsign \"my-key\" (tus/now) \"bXktc3RhdGU=.Z_KAig==.s6VGTnzNyzStOoJpGxtuDVvNwqh6HDfrWId3adn6Q98=\")\n=\u003e \"my-state\"\n; returns `nil` if expired\n; returns `nil` if incorrect signature\n```\n\nArguments for `sign` function:\n\n* The first argument should be the actual private key (_not_ a file). It must be either a `string` or a `byte array`. If your key is in a file, you will have to slurp it yourself.\n* The second argument is the expiry time in epoch seconds. Use `(tus/plus-now some-number)` to set an expiry `some-number` of seconds in the future.\n* The third argument is the state to save. It must be a string.\n\n`sign` will return a URI encoded string that contains the state, the expiry time and a signature.\nThis can be used in for example in a callback URL where you do not want to store temporary state in your service.\nThe tamperproof state URI will survive a service restart as long as the private key is the same.\nAs the return value is URI encoded, you do not need to further encode if including it in e.g. a callback URL.\n\nArguments for `unsign` function:\n\n* The first argument should be a private key. It must be either a `string` or a `byte array`. If your key is in a file, you will have to slurp it yourself.\n* The second argument is the current time in epoch seconds. Use `(tus/now)` to use the current time. This parameter can also be left out. It will then default to the current time. \n* The third argument is the URI encoded string that was returned by `sign`. It must be a string.\n\n`unsign` will return the state string if and only if the signature was correct and it was not expired. Otherwise, it will return `nil`.\n\n## \"Advanced\" usage\n\n```clojure\n(require '[com.github.ivarref.tamperproof-uri-state :as tus])\n\n(def state-with-hash (tus/sign \"my-key\" (tus/plus-now 3600) \"my-state\"))\n\n(let [{:keys [tampered? expired? state]} (tus/unsign-to-map \"my-key\" (tus/now) state-with-hash)]\n  (cond tampered?\n        (log/error \"Call the cops ...\") ; state will be nil\n        \n        expired?\n        (log/warn \"Slow user alert ...\") ; state will be nil\n        \n        state\n        (do\n          (log/info \"Do some work\"))))\n```\n\n### Details\n\n`sign` and `unsign` use `HmacSHA256` for generating a signature/hash.\nThis, combined with the custom encoding, yields a smaller amount of bytes than using a regular JWT would.\nAn external system/user can view the contents of the state, but cannot verify its authenticity.\nThere is not public key here. This means that the recipient of the signed/hashed state should be the same service\nor another service with an identical private key.\n\n\n### IFAQ.\n\n\u003e Did you just \"roll your own crypto\"?\n\nNo, I rolled my own \"Java standard library interop in Clojure\".\n\n\u003e Why?\n\nIt was the least worst (aka best) I could think of.\n\n\u003e IFAQ?\n\nThat is InFrequently Asked Questions.\n\n## Further reading\n\n[HMAC in Java](https://www.baeldung.com/java-hmac).\n\n## License\n\nCopyright © 2025 Ivar Refsdal\n\nDistributed under the Eclipse Public License either version 2.0 or (at\nyour option) any later version.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivarref%2Ftamperproof-uri-state","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivarref%2Ftamperproof-uri-state","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivarref%2Ftamperproof-uri-state/lists"}