{"id":37024319,"url":"https://github.com/paseto-toolkit/jpaseto","last_synced_at":"2026-01-14T02:56:24.029Z","repository":{"id":38237170,"uuid":"235438197","full_name":"paseto-toolkit/jpaseto","owner":"paseto-toolkit","description":"A library for creating and parsing Paseto in Java","archived":false,"fork":false,"pushed_at":"2023-09-04T16:14:50.000Z","size":629,"stargazers_count":70,"open_issues_count":13,"forks_count":15,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-07-04T04:05:06.319Z","etag":null,"topics":["hacktoberfest","java","paseto","paseto-tokens","security"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paseto-toolkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2020-01-21T20:46:32.000Z","updated_at":"2025-04-12T09:18:18.000Z","dependencies_parsed_at":"2023-02-19T09:45:48.728Z","dependency_job_id":null,"html_url":"https://github.com/paseto-toolkit/jpaseto","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/paseto-toolkit/jpaseto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paseto-toolkit%2Fjpaseto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paseto-toolkit%2Fjpaseto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paseto-toolkit%2Fjpaseto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paseto-toolkit%2Fjpaseto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paseto-toolkit","download_url":"https://codeload.github.com/paseto-toolkit/jpaseto/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paseto-toolkit%2Fjpaseto/sbom","scorecard":{"id":721330,"data":{"date":"2025-08-11","repo":{"name":"github.com/paseto-toolkit/jpaseto","commit":"9e7d1d8802c29f1e73094d4cbb55d54d91d04864"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Code-Review","score":-1,"reason":"Found no human activity in the last 16 changesets","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: .mvn/wrapper/maven-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":4,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Warn: no linked content found","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/paseto-toolkit/jpaseto/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/paseto-toolkit/jpaseto/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:50: update your workflow using https://app.stepsecurity.io/secureworkflow/paseto-toolkit/jpaseto/build.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T11:27:24.967Z","repository_id":38237170,"created_at":"2025-08-22T11:27:24.967Z","updated_at":"2025-08-22T11:27:24.967Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408798,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["hacktoberfest","java","paseto","paseto-tokens","security"],"created_at":"2026-01-14T02:56:23.180Z","updated_at":"2026-01-14T02:56:24.010Z","avatar_url":"https://github.com/paseto-toolkit.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[\u003cimg src=\"/docs/jpaseto-logo.png\" align=\"right\" height=\"100px\"/\u003e](https://github.com/paseto-toolkit/jpaseto)\n[![Maven Central](https://img.shields.io/maven-central/v/dev.paseto/jpaseto-api.svg)](https://search.maven.org/search?q=g:dev.paseto)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Security Audit](https://img.shields.io/badge/%EF%B8%8FSecurity%20Audit-%F0%9F%9B%A1-green)](https://paragonie.com/audit/OiT6VlbQ7n6Y6Qz6)\n\n# JPaseto - Paseto Library for Java\n\nJPaseto aims to be the easiest to use and understand library for creating and verifying Paseto tokens on the JVM.\n\nJPaseto is a Java implementation based exclusively on the [Paseto specification](https://paseto.io/rfc/). And is a \ndirect port of [JJWT](https://github.com/jwtk/jjwt/), if you are using JWTs check out that library.\n\nWe've also added some convenience extensions that are not yet part of the specification, such as validation of the \nregistered date claims.\n\nThe goal of this project is to provide a pure Java implementation of the Paseto specification. \n\n## Table of Contents\n\n* [Features](#features)\n  * [Differences Between Other Java Paseto Implementations](#other-options)\n* [Community](#community)\n  * [Getting Help](#help)\n    * [Questions](#help-questions)\n    * [Bugs and Feature Requests](#help-issues)\n  * [Contributing](#contributing)\n    * [Pull Requests](#contributing-pull-requests)\n    * [Help Wanted](#contributing-help-wanted)\n* [What is a Paseto Token?](#overview)\n* [Installation](#install)\n  * [JDK Projects](#install-jdk)\n    * [Maven](#install-jdk-maven)\n    * [Gradle](#install-jdk-gradle)\n    * [Sodium native library](#install-sodium)\n    * [Paseto Format Support](#format-support)\n  * [Understanding JPaseto Dependencies](#install-understandingdependencies)\n* [Quickstart](#quickstart)\n* [Keys and Secrets](#keys-secrets)\n  * [Creating Safe Keys](#key-create)\n    * [Secret Keys](#key-create-secret)\n    * [Asymetric Keys](#key-create-asym)\n  * [Create a Paseto Token](#paseto-create)\n    * [Footer](#paseto-create-footer)\n    * [Claims](#paseto-create-claims)\n      * [Standard Claims](#paseto-create-claims-standard)\n      * [Custom Claims](#paseto-create-claims-custom)\n  * [Read a Paseto Token](#paseto-read)\n    * [Verification Key](#paseto-read-key)\n      * [Find the Verification Key at Runtime](#paseto-read-key-resolver)\n    * [Claims Assertions](#paseto-read-claims)\n    * [Accounting for Clock Skew](#paseto-read-clock)\n      * [Custom Clock](#paseto-read-clock-custom)\n* [JSON Processor](#json)\n  * [Custom JSON Processor](#json-custom)\n  * [Parsing of Custom Claim Types](#json-jackson-custom-types)\n* [Learn More](#learn-more)\n* [License](#license)\n\n\u003ca name=\"features\"\u003e\u003c/a\u003e\n## Features\n\n * Fully functional on all JDKs 1.8+\n * Automatic security best practices and assertions\n * Easy to learn and read API\n * Convenient and readable [fluent](http://en.wikipedia.org/wiki/Fluent_interface) interfaces, great for IDE auto-completion to write code quickly\n * Fully RFC-draft specification compliant on all implemented functionality, tested against RFC-specified test vectors\n * Convenience enhancements beyond the specification such as\n    * Claims assertions (requiring specific values)\n    * Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson) \n    * and more...\n    \n\u003ca name=\"other-options\"\u003e\u003c/a\u003e\n### Differences Between Other Java Paseto Implementations\n\nWhy choose this library over the other Java Paseto implementations?\n\n- Fluent API\n- Full security audited performed by [Paragon Initiative Enterprises](https://paragonie.com/audit/OiT6VlbQ7n6Y6Qz6)\n- Available on Maven Central\n- Low dependency count\n- Already using JJWT, this library works the same way\n\n\u003ca name=\"community\"\u003e\u003c/a\u003e\n## Community\n\n\u003ca name=\"help\"\u003e\u003c/a\u003e\n### Getting Help\n\nIf you have trouble using JPaseto, please first read the documentation on this page before asking questions.  We try \nvery hard to ensure JPaseto's documentation is robust, categorized with a table of contents, and up to date for each release.\n\n\u003ca name=\"help-questions\"\u003e\u003c/a\u003e\n#### Questions\n\nIf the documentation or the API JavaDoc isn't sufficient, and you either have usability questions or are confused\nabout something, please [ask your question here](https://stackoverflow.com/questions/ask?tags=jpaseto\u0026guided=false).\n   \nIf you believe you have found a bug or would like to suggest a feature enhancement, please create a new GitHub issue, \nhowever:\n\n**Please do not create a GitHub issue to ask a question.**\n\nWe use GitHub Issues to track actionable work that requires changes to JPaseto's design and/or codebase.  If you have a \nusability question, instead please \n[ask your question here](https://stackoverflow.com/questions/ask?tags=jpaseto\u0026guided=false).\n\n\u003ca name=\"help-issues\"\u003e\u003c/a\u003e\n#### Bugs and Feature Requests\n\nIf you do not have a usability question and believe you have a legitimate bug or feature request, \nplease do [create a new JPaseto issue](https://github.com/pasetodev/jpaseto/issues/new).\n\nIf you feel like you'd like to help fix a bug or implement the new feature yourself, please read the Contributing \nsection next before starting any work.\n\n\u003ca name=\"contributing\"\u003e\u003c/a\u003e\n### Contributing\n\n\u003ca name=\"contributing-pull-requests\"\u003e\u003c/a\u003e\n#### Pull Requests\n\nSimple Pull Requests that fix anything other than JPaseto core code (documentation, JavaDoc, typos, test cases, etc) are \nalways appreciated and have a high likelihood of being merged quickly. Please send them!\n\nHowever, if you want or feel the need to change JPaseto's functionality or core code, please do not issue a pull request \nwithout [creating a new JPaseto issue](https://github.com/pasetodev/jpaseto/issues/new) and discussing your desired \nchanges **first**, _before you start working on it_.\n\nIt would be a shame to reject your earnest and genuinely appreciated pull request if it might not not align with the \nproject's goals, design expectations or planned functionality.\n\nSo, please [create a new JPaseto issue](https://github.com/pasetodev/jpaseto/issues/new) first to discuss, and then we can see if\n(or how) a PR is warranted.  Thank you!\n\n\u003ca name=\"contributing-help-wanted\"\u003e\u003c/a\u003e\n#### Help Wanted\n\nIf you would like to help, but don't know where to start, please visit the \n[Help Wanted Issues](https://github.com/pasetodev/jpaseto/labels/help%20wanted) page and pick any of the \nones there, and we'll be happy to discuss and answer questions in the issue comments.\n\nIf any of those don't appeal to you, no worries! Any help you would like to offer would be \nappreciated based on the above caveats concerning [contributing pull reqeuests](#contributing-pull-requests). Feel free\nto discuss or ask questions first if you're not sure. :)\n\n\u003ca name=\"overview\"\u003e\u003c/a\u003e\n## What is a Paseto Token?\n\nDon't know what a Paseto Token is? Read on. Otherwise, jump on down to the [Installation](#Installation) section.\n\nPaseto is a means of transmitting information between two parties in a compact, verifiable form.\n\nThe bits of information encoded in the body of a Paseto token are called `claims`. The expanded form of the Paseto is in a JSON format, so each `claim` is a key in the JSON object.\n \nPaseto can be cryptographically signed (\"public\" tokens) or encrypted with a shared secret (\"local\" tokens).\n\nThis adds a powerful layer of verifiability to the user of Paseto tokens. The receiver has a high degree of confidence that the Paseto token has not been tampered with by verifying the signature, for instance.\n\nThe compact representation of a signed Paseto token is a string that has three or four parts, each separated by a `.`:\n\n```\nversion.purpose.payload.footer\n```\n\u003e the footer is optional\n\nThe version is a string that represents the current version of the protocol. Currently, two versions are specified, which each possess their own ciphersuites. Accepted values: v1, v2.\n\nThe purpose is a short string describing the purpose of the token. Accepted values: local, public.\n\n- local: shared-key authenticated encryption\n- public: public-key digital signatures; not encrypted\nAny optional data can be appended to the end. This information is NOT encrypted, but it is used in calculating the authentication tag for the payload. It's always base64url-encoded.\n\n- For local tokens, it's included in the associated data alongside the nonce.\n- For public tokens, it's appended to the message during the actual authentication/signing step, in accordance to our standard format.\nThus, if you want unencrypted, but authenticated, tokens, you can simply set your payload to an empty string and your footer to the message you want to authenticate.\n\nConversely, if you want to support key rotation, you can use the unencrypted footer to store the `kid` claim.\n\nThere are a number of standard claims, called Registered Claims, see section 6.1 \nin [the specification](https://paseto.io/rfc/) and `sub` (for subject) is one of them.\n\nTo compute the signature, you need a secret key to sign it. We'll cover keys later.\n\n\u003ca name=\"install\"\u003e\u003c/a\u003e\n## Installation\n\nUse your favorite Maven-compatible build tool to pull the dependencies from Maven Central.\n\nThe dependencies could differ slightly if you are working with a [JDK project](#install-jdk).\n\n\u003ca name=\"install-jdk\"\u003e\u003c/a\u003e\n### JDK Projects\n\nIf you're building a (non-Android) JDK project, you will want to define the following dependencies:\n\n\u003ca name=\"install-jdk-maven\"\u003e\u003c/a\u003e\n#### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-api\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-impl\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n    \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-jackson\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n    \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e\n\u003c!-- Uncomment the next lines if you want to use Bouncy Castle, supports all Paseto formats --\u003e\n\u003c!-- \n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-bouncy-castle\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n    \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e --\u003e\n\u003c!-- or this (only 'v1.local' tokens) for smaller dependency (~11 KB for HKDF vs. ~4.3 MB for Bouncy Castle) --\u003e\n\u003c!-- \n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-hkdf\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n    \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e --\u003e\n\u003c!-- Uncomment the next lines if you want to use Lib Sodium for v2 local tokens --\u003e\n\u003c!-- NOTE: this requires the native lib sodium library installed on your system see below --\u003e\n\u003c!-- \n\u003cdependency\u003e\n    \u003cgroupId\u003edev.paseto\u003c/groupId\u003e\n    \u003cartifactId\u003ejpaseto-sodium\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n    \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e --\u003e\n```\n\n\u003ca name=\"install-jdk-gradle\"\u003e\u003c/a\u003e\n#### Gradle\n\n```groovy\ndependencies {\n    compile 'dev.paseto:jpaseto-api:0.6.0'\n    runtime 'dev.paseto:jpaseto-impl:0.6.0',\n            'dev.paseto:jpaseto-jackson:0.6.0',\n\n          // Uncomment the next lines if you want to use Bouncy Castle, supports all Paseto formats\n          // 'dev.paseto:jpaseto-bouncy-castle:0.6.0',\n\n          // or this (only 'v1.local' tokens) for smaller dependency (~11 KB for HKDF vs. ~4.3 MB for Bouncy Castle)\n          // 'dev.paseto:jpaseto-hkdf:0.6.0',\n  \n          // Uncomment the next lines if you want to use Lib Sodium for v2 local tokens \n          // NOTE: this requires the native lib sodium library installed on your system see below\n          // 'dev.paseto:jpaseto-sodium:0.6.0', // supports v2 local tokens\n}\n```\n\u003ca name=\"install-sodium\"\u003e\u003c/a\u003e\n#### libsodium\n\nInstallation the a native library [libsodium](https://github.com/jedisct1/libsodium) is required when creating or parseing \"v2.local\" tokens, as an alternative to using `jpaseto-bouncy-castle`.\n\n**NOTE:** `public` tokens can be used with the `jpaseto-bouncy-castle` dependency or Java 11+. `v1.local` tokens require `jpaseto-bouncy-castle` or `jpaseto-hkdf`.\n\n- MacOS -  Can install libsodium using brew: \n  \n  `brew install libsodium`\n\n- Windows - Download [prebuilt binaries](https://download.libsodium.org/libsodium/releases/)\n\n\u003ca name=\"format-support\"\u003e\u003c/a\u003e\n#### Paseto Format Support\n\nAll Paseto formats are supported by JPaseto, the following contains a table of which additional modules are need to support a given token format:\n\n| Module (Artifact Id) | Description | v1.local | v1.public | v2.local | v2.public |\n| -------------------- | ----------- | -------- | --------- | -------- | --------- |\n| no additional modules \u003csup\u003e*\u003c/sup\u003e | [Java Cryptography Architecture (JCA)](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) | ❌ | ✅ | ❌ | ✅ |\n| `jpaseto-bouncy-castle` | [Bouncy Castle](https://www.bouncycastle.org/) | ✅ | ✅ | ✅ | ✅ | \n| `jpaseto-hkdf` | [HKDF](https://github.com/patrickfav/hkdf), minimal dependency size (~11K),  | ✅ | ❌ | ❌ | ❌ |\n| `jpaseto-sodium` | [https://libsodium.gitbook.io/doc/](https://libsodium.gitbook.io/doc/) | ❌ | ❌ | ✅ | ❌ |  \n\n\u003csup\u003e*\u003c/sup\u003e With no additional dependencies `v1.public` and `v2.public` tokens are supported with via the [Java Cryptography Architecture (JCA)](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) API. Generally speaking, without the additional modules listed above `v1.public` tokens require [Java 11 (and some Java 8 distributions)](https://bugs.openjdk.java.net/browse/JDK-8230978), and `v2.public` tokens require [Java 15](https://bugs.openjdk.java.net/browse/JDK-8190219).\n\n**NOTE:** Multiple implementations can be used together, for example using `jpaseto-hkdf` and `jpaseto-sodium` on a 1.8+ JVM would support all token types. \n\n\u003ca name=\"install-understandingdependencies\"\u003e\u003c/a\u003e\n### Understanding JPaseto Dependencies\n\nNotice the above dependency declarations all have only one compile-time dependency and the rest are declared as \n_runtime_ dependencies.\n\nThis is because JPaseto is designed so you only depend on the APIs that are explicitly designed for you to use in\nyour applications and all other internal implementation details - that can change without warning - are relegated to\nruntime-only dependencies.  This is an extremely important point if you want to ensure stable JPaseto usage and\nupgrades over time:\n\n**JPaseto guarantees semantic versioning compatibility for only the `jpaseto-api` .jar.**\n\nThis is done to benefit you: great care goes into curating the `jpaseto-api` .jar and ensuring it contains what you need\nand remains backwards compatible as much as is possible so you can depend on that safely with compile scope.  The \nruntime `jpaseto-impl` .jar strategy affords the JPaseto developers the flexibility to change the internal packages and \nimplementations whenever and however necessary.  This helps us implement features, fix bugs, and ship new releases to \nyou more quickly and efficiently.\n\n\u003ca name=\"quickstart\"\u003e\u003c/a\u003e\n## Quickstart\n\nMost complexity is hidden behind a convenient and readable builder-based [fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), great for relying on IDE auto-completion to write code quickly.  Here's an example:\n\n```java\nimport dev.paseto.jpaseto.Pasetos;\nimport dev.paseto.jpaseto.lang.Keys;\nimport java.security.SecretKey;\n\n// We need a secret key, so we'll create one just for this example. Usually\n// the key would be read from your application configuration instead.\nSecretKey key = Keys.secretKey()\n\nString token = Pasetos.V1.LOCAL.builder()\n    .setSubject(\"Joe\")\n    .setSharedSecret(key)\n    .compact();\n```\n\nHow easy was that!?\n\nIn this case, we are:\n \n 1. *building* a Paseto token that will have the registered claim `sub` (subject) set to `Joe`. We are then\n 2. *encrypted* the Paseto token using a shared secret with AES-256-CTR algorithm.  Finally, we are\n 3. *compacting* it into its final `String` form.\n\nThe resultant `token` String looks like this:\n\n```\nv1.local.CuizxAzVIz5bCqAjsZpXXV5mk_WWGHbVxmdF81DORwyYcMLvzoUHUmS_VKvJ1hn5zXyoMkygkEYLM2LM00uBI3G9gXC5VrZCUM-BLZo1q9IDIncAZTxYkE1NUTMz\n```\n\nNow let's verify the Paseto (parsing tokens with invalid signatures or public keys will throw an exception):\n\n```java\nassert Pasetos.parserBuilder().setSharedSecret(key).build().parse(token).getClaims().getSubject().equals(\"Joe\");\n```\n\nThere are two things going on here. The `key` from before is being used to validate the signature of the token. If it \nfails to verify the token, a `PasetoSignatureException` (which extends from `PasetoException`) is thrown. Assuming the Paseto token is \nvalidated, we parse out the claims and assert that that subject is set to `Joe`.\n\nYou have to love code one-liners that pack a punch!\n\nBut what if parsing or signature validation failed?  You can catch `PasetoSignatureException` and react accordingly:\n\n```java\ntry {\n    Pasetos.parserBuilder().setSharedSecret(key).build().parse(token);\n    //OK, we can trust this token\n} catch (PasetoException e) {\n    //don't trust the token!\n}\n```\n\u003ca name=\"keys-secrets\"\u003e\u003c/a\u003e\n## Keys and Secrets\n\n\u003ca name=\"key-create\"\u003e\u003c/a\u003e\n### Creating Safe Keys\n\nIf you don't want to think about key requirements or just want to make your life easier, JPaseto has\nprovided the `dev.paseto.jpaseto.lang.Keys` utility class that can generate sufficiently secure keys.\n\n\u003ca name=\"key-create-secret\"\u003e\u003c/a\u003e\n#### Secret Keys\n\nIf you want to generate a sufficiently strong `SecretKey` for use with \"local\" tokens, use the \n`Keys.secretKey()` helper method:\n\n```java\nSecretKey key = Keys.secretKey();\n```\n\u003ca name=\"key-create-asym\"\u003e\u003c/a\u003e\n#### Asymmetric Keys\n\nIf you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with \"public\" Ed25519 or RSA\nalgorithms, use the `Keys.keyPairFor(Version)` helper method:\n\n```java\nKeyPair keyPair = Keys.keyPairFor(Version.V1);\n```\n\nYou use the private key (`keyPair.getPrivate()`) to create a token and the public key (`keyPair.getPublic()`) to \nparse/verify a token.\n\n\u003ca name=\"paseto-create\"\u003e\u003c/a\u003e\n### Creating a Paseto Token\n\nYou create a Paseto token as follows:\n\n1. Use one of the `Pasetos.*Builder()` methods to create a `PasetoBuilder` instance.  \n2. Call `PasetoBuilder` methods to add claims as desired.\n3. Specify the `SecretKey` for \"local\" or asymmetric `PrivateKey` for \"public\" tokens.\n4. Finally, call the `compact()` method to compact and encrypt/sign, producing the final token.\n\nFor example:\n\n```java\nString token = Pasetos.V2.LOCAL.builder() // (1)\n    .setSubject(\"Bob\")                     // (2) \n    .setSharedSecret(key)                  // (3)\n    .compact();                            // (4)\n```\n\n\u003ca name=\"paseto-create-footer\"\u003e\u003c/a\u003e\n#### Footer Parameters\n\nA Paseto footer provides metadata about the contents, specifically a `kid` when using rotated keys.\n\nIf you need to set one or more footer parameters, you can simply call\n`PasetBuilder` `footerClaim` one or more times as needed:\n\n```java\nString token = Pasetos.V2.PUBLIC.builder()\n    .setKeyId(\"myKeyId\")\n    .footerClaim(\"other\", \"data\")\n    // ... etc ...\n```\n\nEach time `footerClaim` is called, it simply appends the key-value pair to an internal `footer` instance, \npotentially overwriting any existing identically-named key/value pair.\n\n\u003ca name=\"paseto-create-claims\"\u003e\u003c/a\u003e\n#### Claims\n\nClaims are a Paseto token's 'payload' and contain the information that the Paseto token creator wishes to present to the token recipient(s).\n\n\u003ca name=\"paseto-create-claims-standard\"\u003e\u003c/a\u003e\n##### Standard Claims\n\nThe `PasetoBuilder` provides convenient setter methods for standard registered Claim names defined in the Paseto \nspecification.  They are:\n\n* `setIssuer`: sets the `iss` (Issuer) Claim\n* `setSubject`: sets the `sub` (Subject) Claim\n* `setAudience`: sets the `aud` (Audience) Claim\n* `setExpiration`: sets the `exp` (Expiration Time) Claim\n* `setNotBefore`: sets the `nbf` (Not Before) Claim\n* `setIssuedAt`: sets the `iat` (Issued At) Claim\n* `setTokenId`: sets the `jti` (Token ID) Claim\n* `setKeyId`: sets the `kid` (Key ID) Claim - found in the footer\n\nFor example:\n\n```java\nPasetos.V1.PUBLIC.builder()\n    .setIssuer(\"me\")\n    .setSubject(\"Bob\")\n    .setAudience(\"you\")\n    .setExpiration(expiration)     //a java.time.Instant\n    .setNotBefore(notBefore)       //a java.time.Instant\n    .setIssuedAt(Instant.now())    // for example, now\n    .setTokenId(UUID.randomUUID()) //just an example id\n    /// ... etc ...\n```\n\n\u003ca name=\"paseto-create-claims-custom\"\u003e\u003c/a\u003e\n##### Custom Claims\n\nIf you need to set one or more custom claims that don't match the standard setter method claims shown above, you\ncan simply call `PasetoBuilder` `claim` one or more times as needed:\n\n```java\nPasetos.V2.PUBLIC.builder()\n    .claim(\"hello\", \"world\")\n    // ... etc ...\n```\n\nEach time `claim` is called, it simply appends the key-value pair to an internal `Claims` instance, potentially \noverwriting any existing identically-named key/value pair.\n\nObviously, you do not need to call `claim` for any [standard claim name](#jws-create-claims-standard) and it is \nrecommended instead to call the standard respective setter method as this enhances readability.\n\n\u003ca name=\"paseto-read\"\u003e\u003c/a\u003e\n### Reading a Paseto Token\n\nYou read (parse) a Pasto token as follows:\n\n1. Use the `Pasetos.parserBuilder()` method to create a `PasetoParserBuilder` instance.  \n2. Specify the `SecretKey` (for encrypted \"local\" tokens) or asymmetric `PublicKey` (for signed \"public\" tokens).\u003csup\u003e1\u003c/sup\u003e\n3. Create a reusable and immutable `PasetoParser`.\n4. Finally, call the `parse(String)` method with your token `String`, producing the original token.\n5. The entire call is wrapped in a try/catch block in case parsing or signature validation fails.  We'll cover\n   exceptions and causes for failure later.\n\n\u003csup\u003e1. If you don't know which key to use at the time of parsing, you can look up the key using a `KeyResolver` \nwhich [we'll cover later](#jws-read-key-resolver).\u003c/sup\u003e\n\nFor example:\n\n```java\nPaseto paseto;\n\ntry {\n    paseto = Pasetos.parserBuilder()  // (1)\n        .setSharedSecret(key)         // (2)\n        .build()                      // (3)\n        .parse(tokenString);          // (4)\n    // we can safely trust the token\n} catch (PasetoException ex) {        // (5)\n    // we *cannot* use the token as intended by its creator\n}\n```\n\n\u003ca name=\"paseto-read-key\"\u003e\u003c/a\u003e\n#### Verification Key\n\nThe most important thing to do when reading a Paseto token is to specify the key to use to verify the token's\ncryptographic signature.  If signature verification fails, the Paseto cannot be safely trusted and should be \ndiscarded.\n\nSo which key do we use for verification?\n\n* If the token was encrypted with a `SecretKey` (for \"local\" tokens), the same `SecretKey` should be specified on the `PasetoParserBuilder`.  For example:\n\n  ```java\n  Pasetos.parserBuilder()\n    .setSharedSecret(secretKey) // \u003c----\n    .build()\n    .parse(tokenString);\n  ```\n* If the token was signed with a `PrivateKey` (for \"public\" tokens), that key's corresponding `PublicKey` (not the `PrivateKey`) should be \n  specified on the `PasetoParserBuilder`.  For example:\n\n  ```java\n  Pasetos.parserBuilder()\n    .setPublicKey(publicKey) // \u003c---- publicKey, not privateKey\n    .build()\n    .parse(tokenString);\n  ```\n  \nBut you might have noticed something - what if your application doesn't use just a single SecretKey or KeyPair? What\nif tokens can be created with different `SecretKey`s or public/private keys, or a combination of both?  How do you\nknow which key to specify if you can't inspect the Paseto token first?\n\nIn these cases, you can't call the `PasetoParserBuilder`'s `setSharedSecret` method with a single key - instead, you'll need\nto use a `KeyResolver`, covered next.\n\n\u003ca name=\"paseto-read-key-resolver\"\u003e\u003c/a\u003e\n##### Signing Key Resolver\n\nIf your application expects tokens that can be signed with different keys, you won't call the `setSharedSecret` method.\nInstead, you'll need to implement the \n`KeyResolver` interface and specify an instance on the `PasetoParserBuilder` via the `setKeyResolver` method.  \nFor example:\n\n```java\nKeyResolver KeyResolver = getMyKeyResolver();\n\nPaseto token = Pasetos.parserBuilder()\n    .setKeyResolver(KeyResolver) // \u003c----\n    .build()\n    .parse(tokenString);\n```\n\nYou can simplify things a little by extending from the `KeyResolverAdapter` and implementing the \n`resolvePublicKey(Version, Purpose, FooterClaims)` method.  For example:\n\n```java\npublic class MyKeyResolver extends KeyResolverAdapter {\n    \n    @Override\n    public PublicKey resolvePublicKey(Version version, Purpose purpose, FooterClaims footer) {\n        // implement me\n    }\n}\n```\n\nThe `PasetoParser` will invoke the `resolveSigningKey` method after parsing the payload JSON, but _before verifying the\nsignature_.  This allows you to inspect the `FooterClaims` argument for any information that can\nhelp you look up the `Key` to use for verifying _that specific token_.  This is very powerful for applications\nwith more complex security models that might use different keys at different times or for different users or customers.\n\nWhich data might you inspect?\n\nThe Paseto specification's supported way to do this is to set a `kid` (Key ID) field in the footer when the token is \nbeing created, for example:\n\n```java\n\nPublicKey signingKey = getSigningKey();\nString keyId = getKeyId(signingKey); //any mechanism you have to associate a key with an ID is fine\nString token = Pasetos.V1.PUBLIC.builder()\n    .setKeyId(keyId)            // 1\n    .setPublicKey(signingKey)   // 2\n    .compact();\n```\n\nThen during parsing, your `KeyResolver` can inspect the `FooterClaims` to get the `kid` and then use that value\nto look up the key from somewhere, like a database.  For example:\n\n```java\npublic class MyKeyResolver extends KeyResolverAdapter {\n    \n    @Override\n    public PublicKey resolvePublicKey(Version version, Purpose purpose, FooterClaims footer) {\n        //inspect the footer, lookup and return the signing key\n        String keyId = footer.getKeyId(); //or any other field that you need to inspect\n        PublicKey key = lookupVerificationKey(keyId); //implement me\n        return key;\n    }\n}\n```\n\nNote that inspecting the `footer.getKeyId()` is just the most common approach to look up a key - you could \ninspect any number of footer fields to determine how to lookup the verification key.  It is all based on \nhow the token was created.\n\nFinally remember that for \"local\" tokens a `SecretKey` is used, and for \"public\" tokens a `Public` key is used.\n\n\u003ca name=\"paseto-read-claims\"\u003e\u003c/a\u003e\n#### Claim Assertions\n\nYou can enforce that the Pasto token you are parsing conforms to expectations that you require and are important for your \napplication.\n\nFor example, let's say that you require that the token you are parsing has a specific `sub` (subject) value,\notherwise you may not trust the token.  You can do that by using one of the various `require`* methods on the \n`PasetoParserBuilder`:\n\n```java\ntry {\n    Pasetos.parserBuilder()\n        .requireSubject(\"jsmith\")\n        .setSharedSecret(key)\n        .build()\n        .parse(s);\n} catch(InvalidClaimException ice) {\n    // the sub field was missing or did not have a 'jsmith' value\n}\n```\n\nIf it is important to react to a missing vs an incorrect value, instead of catching `InvalidClaimException`, \nyou can catch either `MissingClaimException` or `IncorrectClaimException`:\n\n```java\ntry {\n    Pasetos.parserBuilder().requireSubject(\"jsmith\").setSharedSecret(key).build().parse(s);\n} catch(MissingClaimException mce) {\n    // the parsed token did not have the sub field\n} catch(IncorrectClaimException ice) {\n    // the parsed token had a sub field, but its value was not equal to 'jsmith'\n}\n```\n\nYou can also require custom fields by using the `require(fieldName, requiredFieldValue)` method - for example:\n\n```java\ntry {\n    Pasetos.parserBuilder().require(\"myfield\", \"myRequiredValue\").setSharedSecret(key).build().parse(s);\n} catch(InvalidClaimException ice) {\n    // the 'myfield' field was missing or did not have a 'myRequiredValue' value\n}\n```\n(or, again, you could catch either `MissingClaimException` or `IncorrectClaimException` instead).\n\nPlease see the `PasetoParserBuilder` class and/or JavaDoc for a full list of the various `require`* methods you may use for claims\nassertions.\n\n\u003ca name=\"paseto-read-clock\"\u003e\u003c/a\u003e\n#### Accounting for Clock Skew\n\nWhen parsing a Paseto token, you might find that `exp` or `nbf` claim assertions fail (throw exceptions) because the clock on \nthe parsing machine is not perfectly in sync with the clock on the machine that created the token.  This can cause \nobvious problems since `exp` and `nbf` are time-based assertions, and clock times need to be reliably in sync for shared\nassertions.\n\nYou can account for these differences (usually no more than a few minutes) when parsing using the `PasetoParserBuilder`'s\n `setAllowedClockSkew`. For example:\n\n```java\nPasetos.parserBuilder()\n    .setAllowedClockSkew(Duration.ofMinutes(3)) // \u003c----\n    // ... etc ...\n    .build()\n    .parse(tokenString);\n```\nThis ensures that clock differences between the machines can be ignored. Two or three minutes should be more than \nenough; it would be fairly strange if a production machine's clock was more than 5 minutes difference from most \natomic clocks around the world.\n\n\u003ca name=\"paseto-read-clock-custom\"\u003e\u003c/a\u003e\n##### Custom Clock Support\n\nIf the above `setAllowedClockSkew` isn't sufficient for your needs, the timestamps created\nduring parsing for timestamp comparisons can be obtained via a custom time source.  Call the `PasetoParserBuilder`'s `setClock`\n method with an implementation of the `java.time.Clock` interface.  For example:\n \n ```java\nClock clock = new MyClock();\n\nPasetos.parserBuilder().setClock(myClock) //... etc ...\n``` \n\nThe `PasetoParser`'s default `Clock` implementation uses `Clock.systemUTC()`, as most would expect.  \nHowever, supplying your own clock could be useful, especially when writing test cases to \nguarantee deterministic behavior.\n\n\u003ca name=\"json\"\u003e\u003c/a\u003e\n## JSON Support\n\nA `PasetoBuilder` will serialize the `Claims` and `FooterClaims` maps (and potentially any Java objects they \ncontain) to JSON with a `Serializer\u003cMap\u003cString, ?\u003e\u003e` instance.  Similarly, a `PasetoParser` will \ndeserialize JSON into the `Claims` and `FooterClaims` using a `Deserializer\u003cMap\u003cString, ?\u003e\u003e` instance.\n\nIf you don't explicitly configure a `PasetoBuilder`'s `Serializer` or a `PasetoParser`'s `Deserializer`, JPaseto will \nautomatically attempt to discover and use the following JSON implementation if found in the runtime classpath.  \n\n1. Jackson: This will automatically be used if you specify `dev.paseto:jpaseto-jackson` as a project runtime \n   dependency.  Jackson supports POJOs as claims with full marshaling/unmarshaling as necessary.\n\n2. Gson: This will be used automatically if you specify `dev.paseto:jpaseto-gson` as a \n   project runtime dependency.\n\n**If you want to use POJOs as claim values, use the `dev.paseto:jpaseto-jackson` dependency** (or implement your own\nSerializer and Deserializer if desired).  **But beware**, Jackson will force a sizable (\u003e 1 MB) dependency to an \nAndroid application thus increasing the app download size for mobile users.\n\n\u003ca name=\"json-custom\"\u003e\u003c/a\u003e\n### Custom JSON Processor\n\nIf you don't want to use JPaseto's runtime dependency approach, or just want to customize how JSON serialization and \ndeserialization works, you can implement the `Serializer` and `Deserializer` interfaces and specify instances of\nthem on the `PasetoBuilder` and `PasetoParser` respectively.  For example:\n\nWhen creating a Paseto token:\n\n```java\nSerializer\u003cMap\u003cString, Object\u003e\u003e serializer = getMySerializer(); //implement me\nPasetos.V2.LOCAL.builder()\n    .setSerializer(serializer)\n    // ... etc ...\n```\n\nWhen reading a Paseto token:\n\n```java\nDeserializer\u003cMap\u003cString, Object\u003e\u003e deserializer = getMyDeserializer(); //implement me\nPasetos.parserBuilder()\n    .setDeserializer(deserializer)\n    // ... etc ...\n```\n\n\u003ca name=\"json-jackson-custom-types\"\u003e\u003c/a\u003e\n### Parsing of Custom Claim Types\n\nBy default JPaseto will only convert simple claim types: String, Instant, Date, Long, Integer, Short and Byte.  If you need to deserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in through a constructor. For example:\n\n```java\nnew JacksonDeserializer(Maps.of(\"user\", User.class).build())\n```\n\nThis would trigger the value in the `user` claim to be deserialized into the custom type of `User`.  Given the claims body of:\n\n```json\n{\n    \"issuer\": \"https://example.com/issuer\",\n    \"user\": {\n      \"firstName\": \"Jill\",\n      \"lastName\": \"Coder\"\n    }\n}\n```\n\nThe `User` object could be retrieved from the `user` claim with the following code:\n\n```java\nPasetos.parserBuilder()\n\n    .setDeserializer(new JacksonDeserializer(Map.of(\"user\", User.class))) // \u003c-----\n    .build()\n    .parse(token)\n    .getClaims()\n    .get(\"user\", User.class); // \u003c-----\n```\n\n\u003ca name=\"learn-more\"\u003e\u003c/a\u003e\n## Learn More\n\n- [Paseto RFC (draft)](https://paseto.io/rfc/)\n- [Paseto.io](https://paseto.io/)\n\n\u003ca name=\"license\"\u003e\u003c/a\u003e\n## License\n\nThis project is open-source via the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaseto-toolkit%2Fjpaseto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaseto-toolkit%2Fjpaseto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaseto-toolkit%2Fjpaseto/lists"}