{"id":37016662,"url":"https://github.com/atholbro/paseto","last_synced_at":"2026-01-14T01:53:50.350Z","repository":{"id":48112126,"uuid":"140753713","full_name":"atholbro/paseto","owner":"atholbro","description":"Java Implementation of Platform-Agnostic Security Tokens - https://paseto.io","archived":false,"fork":false,"pushed_at":"2025-11-26T17:12:44.000Z","size":821,"stargazers_count":39,"open_issues_count":0,"forks_count":9,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-11-29T14:14:24.194Z","etag":null,"topics":["authentication","java","jwt","paseto","paseto-tokens","security","token","token-authetication","token-based-authentication"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atholbro.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-07-12T19:02:06.000Z","updated_at":"2025-11-26T17:10:05.000Z","dependencies_parsed_at":"2022-08-12T18:50:37.177Z","dependency_job_id":null,"html_url":"https://github.com/atholbro/paseto","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/atholbro/paseto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atholbro%2Fpaseto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atholbro%2Fpaseto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atholbro%2Fpaseto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atholbro%2Fpaseto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atholbro","download_url":"https://codeload.github.com/atholbro/paseto/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atholbro%2Fpaseto/sbom","scorecard":{"id":214553,"data":{"date":"2025-08-11","repo":{"name":"github.com/atholbro/paseto","commit":"81022c85baf4cd250ab995cb2a8466625701d326"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","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":"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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"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":"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"}},{"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: MIT License: 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 'master'"],"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"}}]},"last_synced_at":"2025-08-17T01:25:04.608Z","repository_id":48112126,"created_at":"2025-08-17T01:25:04.608Z","updated_at":"2025-08-17T01:25:04.608Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["authentication","java","jwt","paseto","paseto-tokens","security","token","token-authetication","token-based-authentication"],"created_at":"2026-01-14T01:53:49.772Z","updated_at":"2026-01-14T01:53:50.338Z","avatar_url":"https://github.com/atholbro.png","language":"Java","readme":"# PASETO: Platform-Agnostic Security Tokens\n[![Build Status](https://travis-ci.org/atholbro/paseto.svg?branch=master)](https://travis-ci.org/atholbro/paseto)\n[![codecov](https://codecov.io/gh/atholbro/paseto/branch/master/graph/badge.svg)](https://codecov.io/gh/atholbro/paseto)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n \nA Java Implementation of Platform-Agnostic Security Tokens - https://paseto.io\n\nPaseto is everything you love about JOSE (JWT, JWE, JWS) without any of the\n[many design deficits that plague the JOSE standards](https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid).\n\n# Contents\n* [What is Paseto?](#what-is-paseto)\n  * [Key Differences between Paseto and JWT](#key-differences-between-paseto-and-jwt)\n  * [Supported Paseto Features](#supported-paseto-features)\n  * [Motivation](#motivation)\n* [Installation](#installation)\n  * [Gradle](#gradle)\n  * [Maven](#maven)\n* [Usage](#usage)\n  * [A note on the available APIs](#a-note-on-the-available-apis)\n  * [V1 vs V2](#v1-vs-v2)\n  * [JsonToken API](#jsontoken-api)\n\n# What is Paseto?\n\nPaseto (Platform-Agnostic SEcurity TOkens) is a specification and reference implementation\nfor secure stateless tokens.\n\n## Key Differences between Paseto and JWT\n\nUnlike JSON Web Tokens (JWT), which gives developers more than enough rope with which to\nhang themselves, Paseto only allows secure operations. JWT gives you \"algorithm agility\",\nPaseto gives you \"versioned protocols\". It's incredibly unlikely that you'll be able to\nuse Paseto in [an insecure way](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries).\n\n\u003e **Caution:** Neither JWT nor Paseto were designed for\n\u003e [stateless session management](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/).\n\u003e Paseto is suitable for tamper-proof cookies, but cannot prevent replay attacks\n\u003e by itself.\n\n## Supported Paseto Features\n| Feature | Supported |\n| :-------------: | :-: |\n| v1.local | ✓ |\n| v1.public | ✓ |\n| v2.local | ✓ |\n| v2.public | ✓ |\n| JsonToken | ✓ |\n\n## Motivation\nThis library was created to support all Paseto features in a plugable fashion. The JSON, and cryptography code\nfor V1/V2 are separate libraries, which allows their implementation to be replaced with alternatives. For\nexample: the default JSON encoding is provided via Jackson, however if you are already using GSON, then you\ncan provide your own EncodingProvider and call into GSON to encode JSON. At some point an implementation of\nthe cryptographic primitives required for v2 tokens in pure Java may be provided.\n\nCurrently the following providers are available:\n\n|         Name          | Type | Description |\n|:---------------------:| :----: | --------------------------------------------------------------------------------------- |\n| encoding-jackson-json | EncodingProvider | JSON using Jackson |\n|     crypto-v1-bc      | V1CryptoProvider | Cryptography for Paseto V1 Tokens using Bouncy Castle |\n|     crypto-v2-bc      | V2CryptoProvider | Cryptography for Paseto V2 Tokens using Bouncy Castle |\n\n_Note: GSON will be officailly supported in the future as an alternative to Jackson._\n\n# Installation\n### Gradle\n\n```gradle\ndependencies {\n\tcompile 'net.aholbrook.paseto:meta:0.6.1'\n}\n```\n\n# Usage\n### A note on the available APIs\nA high level wrapper around raw Paseto tokens is provided and its usage is encouraged. This high level API implements\nthe Paseto `JsonToken` as described in the RFC. However since JsonToken was just recently made a requirement, other\nPaseto implementations provide access to raw Paseto tokens. As such access to the low level Paseto token API is\navailable and described below should you need to work with raw tokens from another library. Most users should stick\nwith the offical JsonToken (which this library allows you to extend if needed).\n\n### V1 vs V2\nIf your curious as to why Paseto has V1 and V2 tokens, you can find more details in the Paseto RFC (section 3)\n(https://paseto.io/rfc). Basically version 1 tokens are a compatibility mode for legacy systems where the newer\ncryptographic primitives required by version 2 are unavailable. An example might be an Arduino.\n\nAll other systems should use the newest version, which is currently version 2. Paseto versions are not backward\ncompatible, therefore a token created with V1 must be verified with V1 and will fail validation if passed to a\nV2 instance.\n\n### Local and Public\nPaseto tokens come in two varieties: _local_ and _public_. The following table outlines the differences:\n\n|  Type  | Encrypted | Authentication | Asymmetric |\n|--------|:---------:|:--------------:|:----------:|\n|  local |     ✓     |        ✓       |            |\n| public |           |        ✓       |      ✓     |\n\nBoth varieties are protected from modifications (authentication). Local tokens are encrypted, but require the same key for encryption and decryption. Public tokens are not encrypted, meaning that anyone can read their contents, however they can\nbe verified using a public key. This allows a party to verify a public token without having the ability to create a valid\ntoken.\n\n## JsonToken API\n### Getting Started\nLets start with an example of creating a basic Paseto JsonToken. For this example we'll use a local token (encrypted) variant and the latest version (2).\n```\nSymmetricKey key = new SymmetricKey(\"q2N#y,-U3{)/^Bcl#VExy06cL\u003e%t]XJY\".getBytes(StandardCharsets.UTF_8), Version.V2);\nTokenService\u003cToken\u003e tokenService = new LocalTokenService.Builder\u003cToken\u003e(Token.class, () -\u003e key)\n    .withDefaultValidityPeriod(Duration.ofDays(15).toSeconds())\n    .build();\nToken token = new Token();\ntoken.setTokenId(\"example-id\"); // A session key, user id, etc.\nString encoded = tokenService.encode(token);\nSystem.out.println(encoded);\n```\n\nLets break down the example step by step:\n\n```SymmetricKey key = new SymmetricKey(\"q2N#y,-U3{)/^Bcl#VExy06cL\u003e%t]XJY\".getBytes(StandardCharsets.UTF_8), Version.V2);```\n\nTo encode a Paseto token, an encryption key is required. Paseto expects your encryption key to be provided as an\ninstance of SymmetricKey which takes the 32 bytes of the key and the Paseto version. For this example I've just made a\nrandom string of 32 characters and converted it to a byte array.\n\n\n```\nTokenService\u003cToken\u003e tokenService = new LocalTokenService.Builder\u003cToken\u003e(Token.class, () -\u003e key)\n    .withDefaultValidityPeriod(Duration.ofDays(15).toSeconds())\n    .build();\n```\nNext we create an instance of the Paseto LocalTokenService. The TokenService is the interface to the interface to\nhigh level Paseto API. The minimum requirements to work with local Paseto tokens are the encryption key, and the\ntype of token. Paseto uses builders, which allows you to adjust some of the default\nbehaviors when encoding/decoding tokens. In this example we set a default validity period, which automatically sets\nthe token expiration date \u0026 time such that the token is valid for the given duration (15 days in this example).\n\nWe provide the encryption key via a lambda `() -\u003e key` which simply returns the key variable we defined in the\nprevious step _(the interface is LocalTokenService.KeyProvider)_. An interface is used here so that the key can be\nloaded on demand for each encode/decode operation. We'll cover this in more detail below, but for now just know that\nthe high level API will call this lambda each time a token is encoded / decoded.\n\nThe type of token must also be specified as this library allows you to extend the base Token type to add your own\ndata fields (provided they don't conflict with the existing fields). Due to this extensibility we have to tell the\nTokenService which type of token to instantiate when decoding a token.\n\nWe then call `withDefaultValidityPeriod()` on the builder, which allows us to set a default expiration date \u0026 time. When\nset, the service will automatically set the token issue and expiration fields based off the current time, unless already\nset. This reduces the code requirement to create a token, as typically most tokens in an application will have a standard\nvalidity period. If we had excluded the call to `withDefaultValidityPeriod()` then no default would be set, and we'd be\nrequired to set an expiration date \u0026 time.\n\nFinally, a call to build() is made, which creates an instance of the TokenService.\n\n```\nToken token = new Token();\ntoken.setTokenId(\"example-id\"); // Where \"example-id\" is an entry in a database table.\n```\nNext we create a new token and set it's ID to \"example-id\". The token ID resolves to the Paseto jti claim (We call it the\nToken ID claim in this library as its easier to deduce it's meaning). The Token ID claim is used to prevent reply attacks\nby providing a key to lookup if this token has been previously used. The details of this are left to the application\nengineer (like with JWT).\n\n```\nString encoded = tokenService.encode(token);\n```\nThe last step is to call encode with the token. Encode returns a String which contains the contents of the token, encrypted\nand authenticated with the key provided earlier. Do note, that this token is \"salted\" such that repeat calls to encode will\nproduce different ouput, however the contents stored within the token remain the same.\n\nThe contents of the token are now protected from tampering, as any change to the token string will invalidate the cryptographic\nsignature applied. Since this is a \"local\" token, the contents are also encrypted, so secret details can be passed to the user\nbut only read by our software.\n\n### Decoding \u0026 Verifiying\nNow that we've encoded our first Paseto token, lets see how we decrypt the contents and verify that no modifications have been\nmade.\n```\n// continuing from the previous example\nToken decoded = tokenService.decode(encoded);\n```\nThis example continues from the previous example and immediately decrypts and verifies the token. Typically this will be some\ntime later, when the client returns the token to our application. The decode() function verifies the integrity of the token and\ndecrypts its contents. The contents of the token are returned as an object of the same type that was given when encoding the\ntoken.\n\nIf the token was modified by the user, or if the token has expired, then an exception of type PasetoException will be thrown\nand the contents of the token will not be returned. The exception will contain the reason as to why the token failed to decode.\n\n### Token Footers\nIf you need to store data in the token which is authenticated but not encrypted, then you can do so using the token footer. The\ntypical use case for this is picking the encryption key used to sign the token, and a default Footer class (KeyId) is provided\nfor this purpose. Do note, that you should never store the encryption key in the token footer, the KeyId should be set to an\nidentifier which describes the key used, not the key itself. For example you could set the KeyId to the date/time that the key\nwas issued.\n\nYou can also store data that you may need to decode before the token is verified by providing your own footer class (or\nextending KeyId if you need it's functionality as well). For example you could store the user's first name to give them a\npersonalized error message if the token fails to validate.\n\n__Data stored in the token footer is never encrypted and the client can easily read this data by Base64 decoding\nthe token string. The data is protected against tampering so long as you're not using the getFooter() method.__\n\n```\nKeyId footer = new KeyId();\nfooter.setKeyId(\"1\"); // first key we're using\n\t\t\nencoded = tokenService.encode(token, footer);\n\n```\n\nAnd that's all there is to setting a token footer, just provide it to the encode() call. Any type is accepted as a footer, it\ndoes not have to be a KeyId. You could provide a basic string, or an object which will be encoded using JSON.\n\n### Decoding with Footers\nWhen decoding a token that has a footer, you have 4 options:\n- Pass the expected footer value to the Paseto library.\n- Decrypt \u0026 Verify the token, and return both the Token and the decoded Footer.\n- Peek at the footer.\n- Ignore the footer.\n\nWe'll look at each one in order:\n\n#### Decode with an expected footer value\nLet's say you start out with a basic `Token`, then later extend it to your own `CustomToken` and only want to accept the newer\nCustomTokens. One way to approach this is to add the token type as a footer, for this example lets say that footer is the\nstring \"custom\". In this case you only want to accept tokens that have their footer set as \"custom\".\n\nThe Paseto library supports this by passing the expected value into the decode function. The footer will be checked before\ndecoding the token using constant time equals to protect against timing attacks. If the footer present in the token does\nnot match the given footer, then an exception will be thrown and the `decode()` operation will fail. This allows you to safely\nensure that the footer contents equals a specific value.\n\n```\ndecoded = tokenService.decode(encoded, footer);\n```\n\n_Note that you may run into trouble if using JSON encoding for the footer as the field order may change. With Jackson you can\ndefine the field order by using the @JsonPropertyOrder annotation. Other libraries may not support this._\n\n#### Return the token and the footer\nIf you need access to the data in the footer beyond ensuring that its equal to an expected value, then you can return the footer\nwith the decoded token by passing the class of the footer type to the `decodeWithFooter()` method. The Token and Footer are\nreturned in a Tuple object \"TokenWithFooter\" which stores both values.\n\n```\nTokenWithFooter twf = tokenService.decodeWithFooter(encoded, KeyId.class);\ndecoded = twf.getToken();\nfooter = twf.getFooter();\n```\n\n#### Peak at the footer\nIf you need to check the value before decoding the token then you can use the getFooter() method.\n\n```\nfooter = tokenService.getFooter(encoded, KeyId.class);\n```\n__No authentication is performed when \"peeking\" at the footer, and therefore getFooter() should only be used when it\ndoes not matter if the value has been tampered with.__\n\n#### Ignore the footer\nIf you don't need access to the footer, then you can use the regular `decode()` method which will silently ignore the footer.\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatholbro%2Fpaseto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatholbro%2Fpaseto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatholbro%2Fpaseto/lists"}