{"id":15068770,"url":"https://github.com/fusionauth/fusionauth-jwt","last_synced_at":"2025-10-06T14:46:06.891Z","repository":{"id":37664650,"uuid":"70124640","full_name":"FusionAuth/fusionauth-jwt","owner":"FusionAuth","description":"A simple to use Java 8 JWT Library. Verify, Sign, Encode, Decode all day.","archived":false,"fork":false,"pushed_at":"2024-07-29T15:50:33.000Z","size":790,"stargazers_count":184,"open_issues_count":8,"forks_count":43,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-03-28T05:28:23.742Z","etag":null,"topics":["der","elliptic","fusionauth-jwt","hmac","java8","jwa","jwk","jwks","jwt","jwt-signer","jwt-signing","jwt-verifier","pem","pem-decoding","rsa","rsa-pss"],"latest_commit_sha":null,"homepage":"https://fusionauth.io","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/FusionAuth.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-06T05:10:48.000Z","updated_at":"2025-03-27T05:58:53.000Z","dependencies_parsed_at":"2022-08-12T12:50:28.765Z","dependency_job_id":"d51075a9-92b8-42d3-aa65-39b87568c27e","html_url":"https://github.com/FusionAuth/fusionauth-jwt","commit_stats":{"total_commits":309,"total_committers":11,"mean_commits":28.09090909090909,"dds":0.5177993527508091,"last_synced_commit":"206a71a6e4a15267968c77a4603d1925c65db3d7"},"previous_names":["inversoft/prime-jwt"],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FusionAuth%2Ffusionauth-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FusionAuth%2Ffusionauth-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FusionAuth%2Ffusionauth-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FusionAuth%2Ffusionauth-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FusionAuth","download_url":"https://codeload.github.com/FusionAuth/fusionauth-jwt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247249532,"owners_count":20908212,"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":["der","elliptic","fusionauth-jwt","hmac","java8","jwa","jwk","jwks","jwt","jwt-signer","jwt-signing","jwt-verifier","pem","pem-decoding","rsa","rsa-pss"],"created_at":"2024-09-25T01:39:12.671Z","updated_at":"2025-10-06T14:46:06.874Z","avatar_url":"https://github.com/FusionAuth.png","language":"Java","readme":"## FusionAuth JWT ![semver 2.0.0 compliant](https://img.shields.io/badge/semver-2.0.0-brightgreen.svg?style=flat-square) ![Tests](https://github.com/FusionAuth/fusionauth-jwt/workflows/Tests/badge.svg)\nFusionAuth JWT is intended to be fast and easy to use. FusionAuth JWT has a single external dependency on Jackson, no Bouncy Castle, Apache Commons or Guava.\n\n## Security disclosures\nIf you find a vulnerability or other security related bug, [please report the vulnerability here](https://fusionauth.io/security) before opening a GitHub issue. This will allow us to assess the disclosure and prepare a fix prior to a public disclosure.\n\nWe are very interested in compensating anyone that can identify a security related bug or vulnerability and properly disclose it to us.\n\n## Features\n - JWT signing using HMAC, RSA and Elliptic Curve support\n   - `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`\n - JWT signing using RSA-PSS signatures\n   - `PS256`, `PS384`, `PS512`\n   - Requires Java 8 update 251 or greater, or any version that includes support RSASSA-PSS\n   - https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146293\n   - Available in versions \u003e= 3.5.0\n - Modular crypto provider so you can drop in support for BC FIPS or other JCE security providers.   \n - PEM decoding / encoding\n   - Decode PEM files to PrivateKey or PublicKey\n     - Decode private EC keys un-encapsulated in PKCS#8, returned PEM will be in PKCS#8 form.\n     - Both public and private keys will be returned when encoded in the private PEM\n   - Encode PrivateKey or PublicKey to PEM\n - JSON Web Key \n   - Build JWK from Private Key\n   - Build JWK from Public Key\n   - Build JWK from PEM\n   - Parse public keys from a JSON Web Key\n   - Retrieve JWK from JWKS endpoints\n - Helpers\n   - Generate RSA Key Pairs in `2048`, `3072` or `4096` bit sizes\n   - Generate EC Key Pairs in `256`, `384` and `521` bit sizes\n   - Generate `x5t` and `x5t#256` values from X.509 Certificates\n   - Generate JWK thumbprint using `SHA-1` or `SHA-256` \n   - Generate ideal HMAC secret lengths for `SHA-256`, `SHA-384` and `SHA-512`\n   - Generate the `at_hash` and `c_hash` claims for OpenID Connect\n\n## Get it\n\n### Maven\n ```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.fusionauth\u003c/groupId\u003e\n  \u003cartifactId\u003efusionauth-jwt\u003c/artifactId\u003e\n  \u003cversion\u003e5.3.3\u003c/version\u003e\n\u003c/dependency\u003e\n ```\n\n### Gradle\n```groovy\nimplementation 'io.fusionauth:fusionauth-jwt:5.3.3'\n```\n\n### Gradle Kotlin\n```kotlin\nimplementation(\"io.fusionauth:fusionauth-jwt:5.3.3\")\n```\n\n### Savant \n```groovy\ndependency(id: \"io.fusionauth:fusionauth-jwt:5.3.3\")\n```\n\nFor others see [https://search.maven.org](https://search.maven.org/artifact/io.fusionauth/fusionauth-jwt/4.0.1/jar).\n \n## Example Code:\n\n### JWT Signing and Verifying\n\n#### Sign and encode a JWT using HMAC\n```java\n// Build an HMAC signer using a SHA-256 hash\nSigner signer = HMACSigner.newSHA256Signer(\"too many secrets\");\n\n// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)\nJWT jwt = new JWT().setIssuer(\"www.acme.com\")\n                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))\n                   .setSubject(\"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\")\n                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));\n                       \n// Sign and encode the JWT to a JSON string representation\nString encodedJWT = JWT.getEncoder().encode(jwt, signer);\n```\n\nA higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.\n```java\n// Build an HMAC signer using a SHA-384 hash\nSigner signer384 = HMACSigner.newSHA384Signer(\"too many secrets\");\n\n// Build an HMAC signer using a SHA-512 hash\nSigner signer512 = HMACSigner.newSHA512Signer(\"too many secrets\");\n```\n\n#### Verify and decode a JWT using HMAC\n```java\n// Build an HMC verifier using the same secret that was used to sign the JWT\nVerifier verifier = HMACVerifier.newVerifier(\"too many secrets\");\n\n// Verify and decode the encoded string JWT to a rich object\nJWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);\n\n// Assert the subject of the JWT is as expected\nassertEquals(jwt.subject, \"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\");\n```\n\n#### Sign and encode a JWT using RSA\n```java\n// Build an RSA signer using a SHA-256 hash. A signer may also be built using the PrivateKey object.\nSigner signer = RSASigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n\n// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)\nJWT jwt = new JWT().setIssuer(\"www.acme.com\")\n                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))\n                   .setSubject(\"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\")\n                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));\n        \n// Sign and encode the JWT to a JSON string representation\nString encodedJWT = JWT.getEncoder().encode(jwt, signer);\n```\n\nA higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.\n```java\n// Build an RSA signer using a SHA-384 hash\nSigner signer = RSASigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n\n// Build an RSA signer using a SHA5124 hash\nSigner signer = RSASigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n```\n\n#### Verify and decode a JWT using RSA\n```java\n// Build an RSA verifier using an RSA Public Key. A verifier may also be built using the PublicKey object.\nVerifier verifier = RSAVerifier.newVerifier(Paths.get(\"public_key.pem\"));\n\n// Verify and decode the encoded string JWT to a rich object\nJWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);\n\n// Assert the subject of the JWT is as expected\nassertEquals(jwt.subject, \"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\");\n```\n\n#### Sign and encode a JWT using EC\n```java\n// Build an EC signer using a SHA-256 hash. A signer may also be built using the PrivateKey object.\nSigner signer = ECSigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n\n// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)\nJWT jwt = new JWT().setIssuer(\"www.acme.com\")\n                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))\n                   .setSubject(\"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\")\n                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));\n        \n// Sign and encode the JWT to a JSON string representation\nString encodedJWT = JWT.getEncoder().encode(jwt, signer);\n```\n\nA higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.\n```java\n// Build an EC signer using a SHA-384 hash\nSigner signer = ECSigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n\n// Build an EC signer using a SHA-512 hash\nSigner signer = ECSigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))));\n```\n\n#### Verify and decode a JWT using EC\n```java\n// Build an EC verifier using an EC Public Key. A verifier may also be built using the PublicKey object.\nVerifier verifier = ECVerifier.newVerifier(Paths.get(\"public_key.pem\"));\n\n// Verify and decode the encoded string JWT to a rich object\nJWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);\n\n// Assert the subject of the JWT is as expected\nassertEquals(jwt.subject, \"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\");\n```\n\n#### Verify a JWT adjusting for Clock Skew\n```java\n// Build an EC verifier using an EC Public Key\nVerifier verifier = ECVerifier.newVerifier(Paths.get(\"public_key.pem\"));\n\n// Verify and decode the encoded string JWT to a rich object and allow up to 60 seconds\n// of clock skew when asserting the 'exp' and 'nbf' claims if they exist.\nJWT jwt = JWT.getDecoder().withClockSkew(60).decode(encodedJWT, verifier);\n\n// Assert the subject of the JWT is as expected\nassertEquals(jwt.subject, \"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\");\n```\n\n#### Verify an expired JWT by going back in time\nIn a scenario where you may have a hard coded JWT in a test case that you wish to validate, you may use the time machine JWT decoder. Ideally you would not hard code JWTs in your tests and instead generate a new one each time so that the JWT would pass the expiration check. If this is not possible, this option is provided.\n```java\n// Build an EC verifier using an EC Public Key\nVerifier verifier = ECVerifier.newVerifier(Paths.get(\"public_key.pem\"));\n\n// Using the time machine decoder, you may adjust 'now' to any point in the past, or future.\n// Note, this is only provided for testing, and should not be used in production.\nZonedDateTime thePast = ZonedDateTime.of(2019, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC) \nJWT jwt = JWT.getTimeMachineDecoder(thePast).decode(encodedJWT, verifier);\n\n// Assert the subject of the JWT is as expected\nassertEquals(jwt.subject, \"f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3\");\n```\n\n\n### Build a Signer, or a Verifier using a provided CryptoProvider\n\nThis pattern is available on the HMAC, RSA and EC verifier and signers.\n \n```java\n// Build and EC signer using a BC Fips ready Crypto Provider\nSigner signer = ECSigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get(\"private_key.pem\"))), new BCFIPSCryptoProvider());\n\n// Build an EC verifier using a BC Fips ready Crypto Provider\nVerifier verifier = ECVerifier.newVerifier(Paths.get(\"public_key.pem\"), new BCFIPSCryptoProvider());\n```\n\n## JSON Web Keys\n\n### Retrieve JSON Web Keys from a JWKS endpoint\n\n```java\n// Retrieve JSON Web Keys using a known JWKS endpoint\n// - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection.\nList\u003cJSONWebKey\u003e keys = JSONWebKeySetHelper.retrieveKeysFromJWKS(\"https://www.googleapis.com/oauth2/v3/certs\");\n\n// Retrieve JSON Web Keys using a well known OpenID Connect configuration endpoint\n// - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection.\nList\u003cJSONWebKey\u003e keys = JSONWebKeySetHelper.retrieveKeysFromWellKnownConfiguration(\"https://accounts.google.com/.well-known/openid-configuration\");\n\n// Retrieve JSON Web Keys using an OpenID Connect issuer endpoint\nList\u003cJSONWebKey\u003e keys = JSONWebKeySetHelper.retrieveKeysFromIssuer(\"https://accounts.google.com\");\n```\n\n### Convert a Public Key to JWK\n\n```java\nJSONWebKey jwk = JSONWebKey.build(publicKey);\nString json = jwk.toJSON();\n```\n\n```json\n{\n  \"e\": \"AQAB\",\n  \"kty\": \"RSA\",\n  \"n\": \"Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U\",\n  \"use\": \"sig\"\n}\n```\n\n### Extract the Public Key from a JWK\n\n```json\n{\n  \"e\": \"AQAB\",\n  \"kty\": \"RSA\",\n  \"n\": \"Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U\",\n  \"use\": \"sig\"\n}\n```\n\n```java\nString json = { ... example above ... }\nbyte[] bytes = json.getBytes(StandardCharsets.UTF_8);\nJSONWebKey jwk = Mapper.deserialize(bytes, JSONWebKey.class);\nPublicKey publicKey = JSONWebKey.parse(jwk);\n```\n\n### Convert a Private Key to JWK\n\n```java\nJSONWebKey jwk = JSONWebKey.build(privateKey);\nString json = jwk.toJSON();\n```\n\n```json\n{\n  \"p\": \"9dy6wUxA0eOHopUP-E5QjDzuW8rXdaQMR566oDJ1qL0iD0koQAB9X3hboB-2Rru0aATu6WDW-jd4mgtYnXO8ow\",\n  \"kty\": \"RSA\",\n  \"q\": \"6Nfc6c8meTRkVRAHCF24LB5GLfsjoMB0tOeEO9w9Ous1a4o-D24bAePMUImAp3woFoNDRfWtlNktOqLel5Pjew\",\n  \"d\": \"C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0\",\n  \"e\": \"AQAB\",\n  \"use\": \"sig\",\n  \"qi\": \"XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw\",\n  \"dp\": \"32QGgDmjr9GX3N6p2wh1YWa_gMHmUSqUScLseUA_7eijeNYU70pCoCtAvVXzDYPhoJ3S4lQuIL2kI_tpMe8GFw\",\n  \"dq\": \"21tJjqeN-k-mWhCwX2xTbpTSzsyy4uWMzUTy6aXxtUkTWY2yK70yClS-Df2MS70G0za0MPtjnUAAgSYhB7HWcw\",\n  \"n\": \"359ZykLITko_McOOKAtpJRVkjS5itwZxzjQidW2X6tBEOYCH4LZbwfj8fGGvlUtzpyuwnYuIlNX8TvZLTenOk45pphXr5PMCMKi7YZgkhd6_t_oeHnXY-4bnDLF1r9OUFKwj6C-mFFM-woKc-62tuK6QJiuc-5bFfn9wRL15K1E\"\n}\n```\n\n### Add a custom property to a JWK\n\n```java\nJSONWebKey jwk = JSONWebKey.build(privateKey)\n                           .add(\"boom\", \"goes the dynamite\")\n                           .add(\"more\", \"cowbell\");\nString json = jwk.toJSON();\n```\n\n```json\n{\n  \"alg\" : \"ES256\",\n  \"boom\" : \"goes the dynamite\",\n  \"crv\" : \"P-256\",\n  \"kty\" : \"EC\",\n  \"more\" : \"cowbell\",\n  \"use\" : \"sig\",\n  \"x\" : \"NIWpsIea0qzB22S0utDG8dGFYqEInv9C7ZgZuKtwjno\",\n  \"y\" : \"iVFFtTgiInz_fjh-n1YqbibnUb2vtBZFs3wPpQw3mc0\"\n}\n```\n\n## Building\n \n## Building with Maven\n ```bash\n $ mvn install\n ```\n\n\n## Building with Savant\n\n```bash\n$ sb int\n```\n\n**Note:** If you do not yet have Savant build tool installed, use the following instructions.\n\n```bash\nmkdir ~/savant\ncd ~/savant\nwget http://savant.inversoft.org/org/savantbuild/savant-core/2.0.2/savant-2.0.2.tar.gz\ntar xvfz savant-2.0.2.tar.gz\nln -s ./savant-2.0.2 current\nexport PATH=$PATH:~/savant/current/bin/\n```\n\nFor more information, checkout [savantbuild.org](http://savantbuild.org/).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffusionauth%2Ffusionauth-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffusionauth%2Ffusionauth-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffusionauth%2Ffusionauth-jwt/lists"}