[
  {
    "path": ".github/CODEOWNERS",
    "content": "*  @oxisto @mfridman\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  categories:\n    - title: 🔐 Features\n      labels:\n        - '*'\n      exclude:\n        labels:\n          - dependencies\n    - title: 👒 Dependencies\n      labels:\n        - dependencies\n"
  },
  {
    "path": ".github/spell-check.toml",
    "content": "[default]\nextend-ignore-re = [\n    # Ignore JWT/JWE tokens\n    \"\\\\beyJ[\\\\dA-Za-z=_-]+(?:\\\\.[\\\\dA-Za-z=_-]{3,}){1,4}\",\n]\n\n[default.extend-identifiers]\n# Common registered claim abbreviations\nalg = \"alg\"\ntyp = \"typ\"\niss = \"iss\"\nsub = \"sub\"\naud = \"aud\"\nexp = \"exp\"\nnbf = \"nbf\"\niat = \"iat\"\njti = \"jti\"\ncty = \"cty\"\n\n# Custom exceptions (i.e. Github Usernames, proper nouns etc)\nthaJeztah = \"thaJeztah\""
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: build\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    types: [opened, synchronize, reopened]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        go: [\"1.21\", \"1.22\", \"1.23\", \"1.24\", \"1.25\"]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: \"${{ matrix.go }}\"\n          check-latest: true\n      - name: Check Go code formatting\n        run: |\n          if [ \"$(gofmt -s -l . | wc -l)\" -gt 0 ]; then\n            gofmt -s -l .\n            echo \"Please format Go code by running: go fmt ./...\"\n            exit 1\n          fi\n      - name: Install tparse\n        run: |\n          mkdir -p $HOME/.local/bin\n          curl -L -o $HOME/.local/bin/tparse https://github.com/mfridman/tparse/releases/latest/download/tparse_linux_x86_64\n          chmod +x $HOME/.local/bin/tparse\n          echo \"$HOME/.local/bin\" >> \"$GITHUB_PATH\"\n      - name: Build\n        run: |\n          go vet ./...\n          go test -v -race -count=1 -json -cover ./... | tee output.json | tparse -follow -notests || true\n          tparse -format markdown -file output.json -all > $GITHUB_STEP_SUMMARY\n          go build ./...\n  coverage:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Setup Go\n        uses: actions/setup-go@v6\n      - name: Coverage\n        run: |\n          go test -v -covermode=count -coverprofile=coverage.cov ./...\n      - name: Coveralls\n        uses: coverallsapp/github-action@v2\n        with:\n          file: coverage.cov\n          format: golang\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [main]\n  pull_request:\n  schedule:\n    - cron: \"31 10 * * 5\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"go\"]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4\n        with:\n          languages: ${{ matrix.language }}\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n          # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v4\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 https://git.io/JvXDl\n\n      # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n      #    and modify them (or add more) to build your code if your project\n      #    uses a compiled language\n\n      #- run: |\n      #   make bootstrap\n      #   make release\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: golangci\non:\n  push:\n    branches:\n      - main\n  pull_request:\njobs:\n  golangci:\n    name: lint\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: \"1.25\"\n          check-latest: true\n      - name: golangci-lint\n        uses: golangci/golangci-lint-action@v9\n        with:\n          # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version\n          version: latest\n\n          # Optional: working directory, useful for monorepos\n          # working-directory: somedir\n\n          # Optional: golangci-lint command line arguments.\n          # args: --issues-exit-code=0\n\n          # Optional: show only new issues if it's a pull request. The default value is `false`.\n          # only-new-issues: true\n\n          # Optional: if set to true then the all caching functionality will be complete disabled,\n          #           takes precedence over all other caching options.\n          # skip-cache: true\n\n          # Optional: if set to true then the action don't cache or restore ~/go/pkg.\n          # skip-pkg-cache: true\n\n          # Optional: if set to true then the action don't cache or restore ~/.cache/go-build.\n          # skip-build-cache: true\n"
  },
  {
    "path": ".github/workflows/spell-check.yml",
    "content": "name: spelling\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\nenv:\n  CLICOLOR: 1\n\njobs:\n  spelling:\n    name: Spell Check with Typos\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Actions Repository\n        uses: actions/checkout@v6\n      - name: Spell Check Repo\n        uses: crate-ci/typos@65120634e79d8374d1aa2f27e54baa0c364fff5a # v1.42.1\n        with:\n          config: ./.github/spell-check.toml\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nbin\n.idea/\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012 Dave Grijalva\nCopyright (c) 2021 golang-jwt maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "MIGRATION_GUIDE.md",
    "content": "# Migration Guide (v5.0.0)\n\nVersion `v5` contains a major rework of core functionalities in the `jwt-go`\nlibrary. This includes support for several validation options as well as a\nre-design of the `Claims` interface. Lastly, we reworked how errors work under\nthe hood, which should provide a better overall developer experience.\n\nStarting from [v5.0.0](https://github.com/golang-jwt/jwt/releases/tag/v5.0.0),\nthe import path will be:\n\n    \"github.com/golang-jwt/jwt/v5\"\n\nFor most users, changing the import path *should* suffice. However, since we\nintentionally changed and cleaned some of the public API, existing programs\nmight need to be updated. The following sections describe significant changes\nand corresponding updates for existing programs.\n\n## Parsing and Validation Options\n\nUnder the hood, a new `Validator` struct takes care of validating the claims. A\nlong awaited feature has been the option to fine-tune the validation of tokens.\nThis is now possible with several `ParserOption` functions that can be appended\nto most `Parse` functions, such as `ParseWithClaims`. The most important options\nand changes are:\n  * Added `WithLeeway` to support specifying the leeway that is allowed when\n    validating time-based claims, such as `exp` or `nbf`.\n  * Changed default behavior to not check the `iat` claim. Usage of this claim\n    is OPTIONAL according to the JWT RFC. The claim itself is also purely\n    informational according to the RFC, so a strict validation failure is not\n    recommended. If you want to check for sensible values in these claims,\n    please use the `WithIssuedAt` parser option.\n  * Added `WithAudience`, `WithSubject` and `WithIssuer` to support checking for\n    expected `aud`, `sub` and `iss`.\n  * Added `WithStrictDecoding` and `WithPaddingAllowed` options to allow\n    previously global settings to enable base64 strict encoding and the parsing\n    of base64 strings with padding. The latter is strictly speaking against the\n    standard, but unfortunately some of the major identity providers issue some\n    of these incorrect tokens. Both options are disabled by default.\n\n## Changes to the `Claims` interface\n\n### Complete Restructuring\n\nPreviously, the claims interface was satisfied with an implementation of a\n`Valid() error` function. This had several issues:\n  * The different claim types (struct claims, map claims, etc.) then contained\n    similar (but not 100 % identical) code of how this validation was done. This\n    lead to a lot of (almost) duplicate code and was hard to maintain\n  * It was not really semantically close to what a \"claim\" (or a set of claims)\n    really is; which is a list of defined key/value pairs with a certain\n    semantic meaning.\n\nSince all the validation functionality is now extracted into the validator, all\n`VerifyXXX` and `Valid` functions have been removed from the `Claims` interface.\nInstead, the interface now represents a list of getters to retrieve values with\na specific meaning. This allows us to completely decouple the validation logic\nwith the underlying storage representation of the claim, which could be a\nstruct, a map or even something stored in a database.\n\n```go\ntype Claims interface {\n\tGetExpirationTime() (*NumericDate, error)\n\tGetIssuedAt() (*NumericDate, error)\n\tGetNotBefore() (*NumericDate, error)\n\tGetIssuer() (string, error)\n\tGetSubject() (string, error)\n\tGetAudience() (ClaimStrings, error)\n}\n```\n\nUsers that previously directly called the `Valid` function on their claims,\ne.g., to perform validation independently of parsing/verifying a token, can now\nuse the `jwt.NewValidator` function to create a `Validator` independently of the\n`Parser`.\n\n```go\nvar v = jwt.NewValidator(jwt.WithLeeway(5*time.Second))\nv.Validate(myClaims)\n```\n\n### Supported Claim Types and Removal of `StandardClaims`\n\nThe two standard claim types supported by this library, `MapClaims` and\n`RegisteredClaims` both implement the necessary functions of this interface. The\nold `StandardClaims` struct, which has already been deprecated in `v4` is now\nremoved.\n\nUsers using custom claims, in most cases, will not experience any changes in the\nbehavior as long as they embedded `RegisteredClaims`. If they created a new\nclaim type from scratch, they now need to implemented the proper getter\nfunctions.\n\n### Migrating Application Specific Logic of the old `Valid`\n\nPreviously, users could override the `Valid` method in a custom claim, for\nexample to extend the validation with application-specific claims. However, this\nwas always very dangerous, since once could easily disable the standard\nvalidation and signature checking.\n\nIn order to avoid that, while still supporting the use-case, a new\n`ClaimsValidator` interface has been introduced. This interface consists of the\n`Validate() error` function. If the validator sees, that a `Claims` struct\nimplements this interface, the errors returned to the `Validate` function will\nbe *appended* to the regular standard validation. It is not possible to disable\nthe standard validation anymore (even only by accident).\n\nUsage examples can be found in [example_test.go](./example_test.go), to build\nclaims structs like the following.\n\n```go\n// MyCustomClaims includes all registered claims, plus Foo.\ntype MyCustomClaims struct {\n\tFoo string `json:\"foo\"`\n\tjwt.RegisteredClaims\n}\n\n// Validate can be used to execute additional application-specific claims\n// validation.\nfunc (m MyCustomClaims) Validate() error {\n\tif m.Foo != \"bar\" {\n\t\treturn errors.New(\"must be foobar\")\n\t}\n\n\treturn nil\n}\n```\n\n## Changes to the `Token` and `Parser` struct\n\nThe previously global functions `DecodeSegment` and `EncodeSegment` were moved\nto the `Parser` and `Token` struct respectively. This will allow us in the\nfuture to configure the behavior of these two based on options supplied on the\nparser or the token (creation). This also removes two previously global\nvariables and moves them to parser options `WithStrictDecoding` and\n`WithPaddingAllowed`.\n\nIn order to do that, we had to adjust the way signing methods work. Previously\nthey were given a base64 encoded signature in `Verify` and were expected to\nreturn a base64 encoded version of the signature in `Sign`, both as a `string`.\nHowever, this made it necessary to have `DecodeSegment` and `EncodeSegment`\nglobal and was a less than perfect design because we were repeating\nencoding/decoding steps for all signing methods. Now, `Sign` and `Verify`\noperate on a decoded signature as a `[]byte`, which feels more natural for a\ncryptographic operation anyway. Lastly, `Parse` and `SignedString` take care of\nthe final encoding/decoding part.\n\nIn addition to that, we also changed the `Signature` field on `Token` from a\n`string` to `[]byte` and this is also now populated with the decoded form. This\nis also more consistent, because the other parts of the JWT, mainly `Header` and\n`Claims` were already stored in decoded form in `Token`. Only the signature was\nstored in base64 encoded form, which was redundant with the information in the\n`Raw` field, which contains the complete token as base64.\n\n```go\ntype Token struct {\n\tRaw       string                 // Raw contains the raw token\n\tMethod    SigningMethod          // Method is the signing method used or to be used\n\tHeader    map[string]any         // Header is the first segment of the token in decoded form\n\tClaims    Claims                 // Claims is the second segment of the token in decoded form\n\tSignature []byte                 // Signature is the third segment of the token in decoded form\n\tValid     bool                   // Valid specifies if the token is valid\n}\n```\n\nMost (if not all) of these changes should not impact the normal usage of this\nlibrary. Only users directly accessing the `Signature` field as well as\ndevelopers of custom signing methods should be affected.\n\n# Migration Guide (v4.0.0)\n\nStarting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0),\nthe import path will be:\n\n    \"github.com/golang-jwt/jwt/v4\"\n\nThe `/v4` version will be backwards compatible with existing `v3.x.y` tags in\nthis repo, as well as `github.com/dgrijalva/jwt-go`. For most users this should\nbe a drop-in replacement, if you're having troubles migrating, please open an\nissue.\n\nYou can replace all occurrences of `github.com/dgrijalva/jwt-go` or\n`github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually\nor by using tools such as `sed` or `gofmt`.\n\nAnd then you'd typically run:\n\n```\ngo get github.com/golang-jwt/jwt/v4\ngo mod tidy\n```\n\n# Older releases (before v3.2.0)\n\nThe original migration guide for older releases can be found at\nhttps://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md.\n"
  },
  {
    "path": "README.md",
    "content": "# jwt-go\n\n[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml)\n[![Go\nReference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt/v5.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt/v5)\n[![Coverage Status](https://coveralls.io/repos/github/golang-jwt/jwt/badge.svg?branch=main)](https://coveralls.io/github/golang-jwt/jwt?branch=main)\n\nA [go](http://www.golang.org) (or 'golang' for search engine friendliness)\nimplementation of [JSON Web\nTokens](https://datatracker.ietf.org/doc/html/rfc7519).\n\nStarting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0)\nthis project adds Go module support, but maintains backward compatibility with\nolder `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. See the\n[`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information. Version\nv5.0.0 introduces major improvements to the validation of tokens, but is not\nentirely backward compatible. \n\n> After the original author of the library suggested migrating the maintenance\n> of `jwt-go`, a dedicated team of open source maintainers decided to clone the\n> existing library into this repository. See\n> [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a\n> detailed discussion on this topic.\n\n\n**SECURITY NOTICE:** Some older versions of Go have a security issue in the\ncrypto/elliptic. The recommendation is to upgrade to at least 1.15 See issue\n[dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more\ndetail.\n\n**SECURITY NOTICE:** It's important that you [validate the `alg` presented is\nwhat you\nexpect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/).\nThis library attempts to make it easy to do the right thing by requiring key\ntypes to match the expected alg, but you should take the extra step to verify it in\nyour usage.  See the examples provided.\n\n### Supported Go versions\n\nOur support of Go versions is aligned with Go's [version release\npolicy](https://golang.org/doc/devel/release#policy). So we will support a major\nversion of Go until there are two newer major releases. We no longer support\nbuilding jwt-go with unsupported Go versions, as these contain security\nvulnerabilities that will not be fixed.\n\n## What the heck is a JWT?\n\nJWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web\nTokens.\n\nIn short, it's a signed JSON object that does something useful (for example,\nauthentication).  It's commonly used for `Bearer` tokens in Oauth 2.  A token is\nmade of three parts, separated by `.`'s.  The first two parts are JSON objects,\nthat have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648)\nencoded.  The last part is the signature, encoded the same way.\n\nThe first part is called the header.  It contains the necessary information for\nverifying the last part, the signature.  For example, which encryption method\nwas used for signing and what key was used.\n\nThe part in the middle is the interesting bit.  It's called the Claims and\ncontains the actual stuff you care about.  Refer to [RFC\n7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about\nreserved keys and the proper way to add your own.\n\n## What's in the box?\n\nThis library supports the parsing and verification as well as the generation and\nsigning of JWTs.  Current supported signing algorithms are HMAC SHA, RSA,\nRSA-PSS, and ECDSA, though hooks are present for adding your own.\n\n## Installation Guidelines\n\n1. To install the jwt package, you first need to have\n   [Go](https://go.dev/doc/install) installed, then you can use the command\n   below to add `jwt-go` as a dependency in your Go program.\n\n```sh\ngo get -u github.com/golang-jwt/jwt/v5\n```\n\n2. Import it in your code:\n\n```go\nimport \"github.com/golang-jwt/jwt/v5\"\n```\n\n## Usage\n\nA detailed usage guide, including how to sign and verify tokens can be found on\nour [documentation website](https://golang-jwt.github.io/jwt/usage/create/).\n\n## Examples\n\nSee [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v5)\nfor examples of usage:\n\n* [Simple example of parsing and validating a\n  token](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-Parse-Hmac)\n* [Simple example of building and signing a\n  token](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-New-Hmac)\n* [Directory of\n  Examples](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#pkg-examples)\n\n## Compliance\n\nThis library was last reviewed to comply with [RFC\n7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few\nnotable differences:\n\n* In order to protect against accidental use of [Unsecured\n  JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using\n  `alg=none` will only be accepted if the constant\n  `jwt.UnsafeAllowNoneSignatureType` is provided as the key.\n\n## Project Status & Versioning\n\nThis library is considered production ready.  Feedback and feature requests are\nappreciated.  The API should be considered stable.  There should be very few\nbackward-incompatible changes outside of major version updates (and only with\ngood reason).\n\nThis project uses [Semantic Versioning 2.0.0](http://semver.org).  Accepted pull\nrequests will land on `main`.  Periodically, versions will be tagged from\n`main`.  You can find all the releases on [the project releases\npage](https://github.com/golang-jwt/jwt/releases).\n\n**BREAKING CHANGES:** A full list of breaking changes is available in\n`VERSION_HISTORY.md`.  See [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information on updating\nyour code.\n\n## Extensions\n\nThis library publishes all the necessary components for adding your own signing\nmethods or key functions.  Simply implement the `SigningMethod` interface and\nregister a factory method using `RegisterSigningMethod` or provide a\n`jwt.Keyfunc`.\n\nA common use case would be integrating with different 3rd party signature\nproviders, like key management services from various cloud providers or Hardware\nSecurity Modules (HSMs) or to implement additional standards.\n\n| Extension | Purpose                                                                                                  | Repo                                              |\n| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |\n| GCP       | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS)             | https://github.com/someone1/gcp-jwt-go            |\n| AWS       | Integrates with AWS Key Management Service, KMS                                                          | https://github.com/matelang/jwt-go-aws-kms        |\n| JWKS      | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc             |\n| TPM       | Integrates with Trusted Platform Module (TPM)                                                            | https://github.com/salrashid123/golang-jwt-tpm    |\n\n*Disclaimer*: Unless otherwise specified, these integrations are maintained by\nthird parties and should not be considered as a primary offer by any of the\nmentioned cloud providers\n\n## More\n\nGo package documentation can be found [on\npkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt/v5). Additional\ndocumentation can be found on [our project\npage](https://golang-jwt.github.io/jwt/).\n\nThe command line utility included in this project (cmd/jwt) provides a\nstraightforward example of token creation and parsing as well as a useful tool\nfor debugging your own integration. You'll also find several implementation\nexamples in the documentation.\n\n[golang-jwt](https://github.com/orgs/golang-jwt) incorporates a modified version\nof the JWT logo, which is distributed under the terms of the [MIT\nLicense](https://github.com/jsonwebtoken/jsonwebtoken.github.io/blob/master/LICENSE.txt).\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nAs of November 2024 (and until this document is updated), the latest version `v5` is supported. In critical cases, we might supply back-ported patches for `v4`.\n\n## Reporting a Vulnerability\n\nIf you think you found a vulnerability, and even if you are not sure, please report it a [GitHub Security Advisory](https://github.com/golang-jwt/jwt/security/advisories/new). Please try be explicit, describe steps to reproduce the security issue with code example(s).\n\nYou will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem.\n\n## Public Discussions\n\nPlease avoid publicly discussing a potential security vulnerability.\n\nLet's take this offline and find a solution first, this limits the potential impact as much as possible.\n\nWe appreciate your help!\n"
  },
  {
    "path": "VERSION_HISTORY.md",
    "content": "# `jwt-go` Version History\n\nThe following version history is kept for historic purposes. To retrieve the current changes of each version, please refer to the change-log of the specific release versions on https://github.com/golang-jwt/jwt/releases.\n\n## 4.0.0\n\n* Introduces support for Go modules. The `v4` version will be backwards compatible with `v3.x.y`.\n\n## 3.2.2\n\n* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)).\n* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)).\n* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)).\n* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)).\n\n## 3.2.1\n\n* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code\n\t* Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`\n* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160 \n\n#### 3.2.0\n\n* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation\n* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate\n* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before.\n* Deprecated `ParseFromRequestWithClaims` to simplify API in the future.\n\n#### 3.1.0\n\n* Improvements to `jwt` command line tool\n* Added `SkipClaimsValidation` option to `Parser`\n* Documentation updates\n\n#### 3.0.0\n\n* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code\n\t* Dropped support for `[]byte` keys when using RSA signing methods.  This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods.\n\t* `ParseFromRequest` has been moved to `request` subpackage and usage has changed\n\t* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`.  The default value is type `MapClaims`, which is an alias to `map[string]interface{}`.  This makes it possible to use a custom type when decoding claims.\n* Other Additions and Changes\n\t* Added `Claims` interface type to allow users to decode the claims into a custom type\n\t* Added `ParseWithClaims`, which takes a third argument of type `Claims`.  Use this function instead of `Parse` if you have a custom type you'd like to decode into.\n\t* Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage\n\t* Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims`\n\t* Added new interface type `Extractor`, which is used for extracting JWT strings from http requests.  Used with `ParseFromRequest` and `ParseFromRequestWithClaims`.\n\t* Added several new, more specific, validation errors to error type bitmask\n\t* Moved examples from README to executable example files\n\t* Signing method registry is now thread safe\n\t* Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser)\n\n#### 2.7.0\n\nThis will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes.\n\n* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying\n* Error text for expired tokens includes how long it's been expired\n* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM`\n* Documentation updates\n\n#### 2.6.0\n\n* Exposed inner error within ValidationError\n* Fixed validation errors when using UseJSONNumber flag\n* Added several unit tests\n\n#### 2.5.0\n\n* Added support for signing method none.  You shouldn't use this.  The API tries to make this clear.\n* Updated/fixed some documentation\n* Added more helpful error message when trying to parse tokens that begin with `BEARER `\n\n#### 2.4.0\n\n* Added new type, Parser, to allow for configuration of various parsing parameters\n\t* You can now specify a list of valid signing methods.  Anything outside this set will be rejected.\n\t* You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON\n* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go)\n* Fixed some bugs with ECDSA parsing\n\n#### 2.3.0\n\n* Added support for ECDSA signing methods\n* Added support for RSA PSS signing methods (requires go v1.4)\n\n#### 2.2.0\n\n* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`.  Result will now be the parsed token and an error, instead of a panic.\n\n#### 2.1.0\n\nBackwards compatible API change that was missed in 2.0.0.\n\n* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte`\n\n#### 2.0.0\n\nThere were two major reasons for breaking backwards compatibility with this update.  The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations.  There will likely be no required code changes to support this change.\n\nThe second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods.  Not all keys used for all signing methods have a single standard on-disk representation.  Requiring `[]byte` as the type for all keys proved too limiting.  Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys.  Backwards compatibility has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`.\n\nIt is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`.\n\n* **Compatibility Breaking Changes**\n\t* `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct`\n\t* `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct`\n\t* `KeyFunc` now returns `interface{}` instead of `[]byte`\n\t* `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key\n\t* `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key\n* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`.  Specific sizes are now just instances of this type.\n    * Added public package global `SigningMethodHS256`\n    * Added public package global `SigningMethodHS384`\n    * Added public package global `SigningMethodHS512`\n* Renamed type `SigningMethodRS256` to `SigningMethodRSA`.  Specific sizes are now just instances of this type.\n    * Added public package global `SigningMethodRS256`\n    * Added public package global `SigningMethodRS384`\n    * Added public package global `SigningMethodRS512`\n* Moved sample private key for HMAC tests from an inline value to a file on disk.  Value is unchanged.\n* Refactored the RSA implementation to be easier to read\n* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM`\n\n## 1.0.2\n\n* Fixed bug in parsing public keys from certificates\n* Added more tests around the parsing of keys for RS256\n* Code refactoring in RS256 implementation.  No functional changes\n\n## 1.0.1\n\n* Fixed panic if RS256 signing method was passed an invalid key\n\n## 1.0.0\n\n* First versioned release\n* API stabilized\n* Supports creating, signing, parsing, and validating JWT tokens\n* Supports RS256 and HS256 signing methods\n"
  },
  {
    "path": "claims.go",
    "content": "package jwt\n\n// Claims represent any form of a JWT Claims Set according to\n// https://datatracker.ietf.org/doc/html/rfc7519#section-4. In order to have a\n// common basis for validation, it is required that an implementation is able to\n// supply at least the claim names provided in\n// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 namely `exp`,\n// `iat`, `nbf`, `iss`, `sub` and `aud`.\ntype Claims interface {\n\tGetExpirationTime() (*NumericDate, error)\n\tGetIssuedAt() (*NumericDate, error)\n\tGetNotBefore() (*NumericDate, error)\n\tGetIssuer() (string, error)\n\tGetSubject() (string, error)\n\tGetAudience() (ClaimStrings, error)\n}\n"
  },
  {
    "path": "cmd/jwt/README.md",
    "content": "`jwt` command-line tool\n=======================\n\nThis is a simple tool to sign, verify and show JSON Web Tokens from\nthe command line.\n\nThe following will create and sign a token, then verify it and output the original claims:\n\n     echo {\\\"foo\\\":\\\"bar\\\"} | ./jwt -key ../../test/sample_key -alg RS256 -sign - | ./jwt -key ../../test/sample_key.pub -alg RS256 -verify -\n\nKey files should be in PEM format. Other formats are not supported by this tool.\n\nTo simply display a token, use:\n\n    echo $JWT | ./jwt -show -\n\nYou can install this tool with the following command:\n\n     go install github.com/golang-jwt/jwt/v5/cmd/jwt"
  },
  {
    "path": "cmd/jwt/main.go",
    "content": "// A useful example app.  You can use this to debug your tokens on the command line.\n// This is also a great place to look at how you might use this library.\n//\n// Example usage:\n// The following will create and sign a token, then verify it and output the original claims.\n//\n//\techo {\\\"foo\\\":\\\"bar\\\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar (\n\t// Options\n\tflagAlg     = flag.String(\"alg\", \"\", algHelp())\n\tflagKey     = flag.String(\"key\", \"\", \"path to key file or '-' to read from stdin\")\n\tflagCompact = flag.Bool(\"compact\", false, \"output compact JSON\")\n\tflagDebug   = flag.Bool(\"debug\", false, \"print out all kinds of debug data\")\n\tflagClaims  = make(ArgList)\n\tflagHead    = make(ArgList)\n\n\t// Modes - exactly one of these is required\n\tflagSign   = flag.String(\"sign\", \"\", \"path to claims file to sign, '-' to read from stdin, or '+' to use only -claim args\")\n\tflagVerify = flag.String(\"verify\", \"\", \"path to JWT token file to verify or '-' to read from stdin\")\n\tflagShow   = flag.String(\"show\", \"\", \"path to JWT token file to show without verification or '-' to read from stdin\")\n)\n\nfunc main() {\n\t// Plug in Var flags\n\tflag.Var(flagClaims, \"claim\", \"add additional claims. may be used more than once\")\n\tflag.Var(flagHead, \"header\", \"add additional header params. may be used more than once\")\n\n\t// Usage message if you ask for -help or if you mess up inputs.\n\tflag.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, \"Usage of %s:\\n\", os.Args[0])\n\t\tfmt.Fprintf(os.Stderr, \"  One of the following flags is required: sign, verify or show\\n\")\n\t\tflag.PrintDefaults()\n\t}\n\n\t// Parse command line options\n\tflag.Parse()\n\n\t// Do the thing.  If something goes wrong, print error to stderr\n\t// and exit with a non-zero status code\n\tif err := start(); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n}\n\n// Figure out which thing to do and then do that\nfunc start() error {\n\tswitch {\n\tcase *flagSign != \"\":\n\t\treturn signToken()\n\tcase *flagVerify != \"\":\n\t\treturn verifyToken()\n\tcase *flagShow != \"\":\n\t\treturn showToken()\n\tdefault:\n\t\tflag.Usage()\n\t\treturn fmt.Errorf(\"none of the required flags are present. What do you want me to do?\")\n\t}\n}\n\n// Helper func:  Read input from specified file or stdin\nfunc loadData(p string) (_ []byte, retErr error) {\n\tif p == \"\" {\n\t\treturn nil, fmt.Errorf(\"no path specified\")\n\t}\n\n\tvar rdr io.Reader\n\tswitch p {\n\tcase \"-\":\n\t\trdr = os.Stdin\n\tcase \"+\":\n\t\treturn []byte(\"{}\"), nil\n\tdefault:\n\t\tf, err := os.Open(p)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trdr = f\n\t\tdefer func() {\n\t\t\tretErr = errors.Join(retErr, f.Close())\n\t\t}()\n\t}\n\treturn io.ReadAll(rdr)\n}\n\n// Print a json object in accordance with the prophecy (or the command line options)\nfunc printJSON(j any) error {\n\tvar out []byte\n\tvar err error\n\n\tif !*flagCompact {\n\t\tout, err = json.MarshalIndent(j, \"\", \"    \")\n\t} else {\n\t\tout, err = json.Marshal(j)\n\t}\n\n\tif err == nil {\n\t\tfmt.Println(string(out))\n\t}\n\n\treturn err\n}\n\n// Verify a token and output the claims.  This is a great example\n// of how to verify and view a token.\nfunc verifyToken() error {\n\t// get the token\n\ttokData, err := loadData(*flagVerify)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"couldn't read token: %w\", err)\n\t}\n\n\t// trim possible whitespace from token\n\ttokData = regexp.MustCompile(`\\s*$`).ReplaceAll(tokData, []byte{})\n\tif *flagDebug {\n\t\tfmt.Fprintf(os.Stderr, \"Token len: %v bytes\\n\", len(tokData))\n\t}\n\n\t// Parse the token.  Load the key from command line option\n\ttoken, err := jwt.Parse(string(tokData), func(t *jwt.Token) (any, error) {\n\t\tif isNone() {\n\t\t\treturn jwt.UnsafeAllowNoneSignatureType, nil\n\t\t}\n\t\tdata, err := loadData(*flagKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tswitch {\n\t\tcase isEs():\n\t\t\treturn jwt.ParseECPublicKeyFromPEM(data)\n\t\tcase isRs():\n\t\t\treturn jwt.ParseRSAPublicKeyFromPEM(data)\n\t\tcase isEd():\n\t\t\treturn jwt.ParseEdPublicKeyFromPEM(data)\n\t\tdefault:\n\t\t\treturn data, nil\n\t\t}\n\t})\n\n\t// Print an error if we can't parse for some reason\n\tif err != nil {\n\t\treturn fmt.Errorf(\"couldn't parse token: %w\", err)\n\t}\n\n\t// Print some debug data\n\tif *flagDebug {\n\t\tfmt.Fprintf(os.Stderr, \"Header:\\n%v\\n\", token.Header)\n\t\tfmt.Fprintf(os.Stderr, \"Claims:\\n%v\\n\", token.Claims)\n\t}\n\n\t// Print the token details\n\tif err := printJSON(token.Claims); err != nil {\n\t\treturn fmt.Errorf(\"failed to output claims: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// Create, sign, and output a token.  This is a great, simple example of\n// how to use this library to create and sign a token.\nfunc signToken() error {\n\t// get the token data from command line arguments\n\ttokData, err := loadData(*flagSign)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"couldn't read token: %w\", err)\n\t} else if *flagDebug {\n\t\tfmt.Fprintf(os.Stderr, \"Token: %v bytes\", len(tokData))\n\t}\n\n\t// parse the JSON of the claims\n\tvar claims jwt.MapClaims\n\tif err := json.Unmarshal(tokData, &claims); err != nil {\n\t\treturn fmt.Errorf(\"couldn't parse claims JSON: %w\", err)\n\t}\n\n\t// add command line claims\n\tif len(flagClaims) > 0 {\n\t\tfor k, v := range flagClaims {\n\t\t\tclaims[k] = v\n\t\t}\n\t}\n\n\t// get the key\n\tvar key any\n\tif isNone() {\n\t\tkey = jwt.UnsafeAllowNoneSignatureType\n\t} else {\n\t\tkey, err = loadData(*flagKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"couldn't read key: %w\", err)\n\t\t}\n\t}\n\n\t// get the signing alg\n\talg := jwt.GetSigningMethod(*flagAlg)\n\tif alg == nil {\n\t\treturn fmt.Errorf(\"couldn't find signing method: %v\", *flagAlg)\n\t}\n\n\t// create a new token\n\ttoken := jwt.NewWithClaims(alg, claims)\n\n\t// add command line headers\n\tif len(flagHead) > 0 {\n\t\tfor k, v := range flagHead {\n\t\t\ttoken.Header[k] = v\n\t\t}\n\t}\n\n\tswitch {\n\tcase isEs():\n\t\tk, ok := key.([]byte)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"couldn't convert key data to key\")\n\t\t}\n\t\tkey, err = jwt.ParseECPrivateKeyFromPEM(k)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase isRs():\n\t\tk, ok := key.([]byte)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"couldn't convert key data to key\")\n\t\t}\n\t\tkey, err = jwt.ParseRSAPrivateKeyFromPEM(k)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase isEd():\n\t\tk, ok := key.([]byte)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"couldn't convert key data to key\")\n\t\t}\n\t\tkey, err = jwt.ParseEdPrivateKeyFromPEM(k)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tout, err := token.SignedString(key)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error signing token: %w\", err)\n\t}\n\tfmt.Println(out)\n\n\treturn nil\n}\n\n// showToken pretty-prints the token on the command line.\nfunc showToken() error {\n\t// get the token\n\ttokData, err := loadData(*flagShow)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"couldn't read token: %w\", err)\n\t}\n\n\t// trim possible whitespace from token\n\ttokData = regexp.MustCompile(`\\s*$`).ReplaceAll(tokData, []byte{})\n\tif *flagDebug {\n\t\tfmt.Fprintf(os.Stderr, \"Token len: %v bytes\\n\", len(tokData))\n\t}\n\n\ttoken, _, err := jwt.NewParser().ParseUnverified(string(tokData), make(jwt.MapClaims))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"malformed token: %w\", err)\n\t}\n\n\t// Print the token details\n\tfmt.Println(\"Header:\")\n\tif err := printJSON(token.Header); err != nil {\n\t\treturn fmt.Errorf(\"failed to output header: %w\", err)\n\t}\n\n\tfmt.Println(\"Claims:\")\n\tif err := printJSON(token.Claims); err != nil {\n\t\treturn fmt.Errorf(\"failed to output claims: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc isEs() bool {\n\treturn strings.HasPrefix(*flagAlg, \"ES\")\n}\n\nfunc isRs() bool {\n\treturn strings.HasPrefix(*flagAlg, \"RS\") || strings.HasPrefix(*flagAlg, \"PS\")\n}\n\nfunc isEd() bool {\n\treturn *flagAlg == \"EdDSA\"\n}\n\nfunc isNone() bool {\n\treturn *flagAlg == \"none\"\n}\n\nfunc algHelp() string {\n\talgs := jwt.GetAlgorithms()\n\tsort.Strings(algs)\n\n\tvar b strings.Builder\n\tb.WriteString(\"signing algorithm identifier, one of\\n\")\n\tfor i, alg := range algs {\n\t\tif i > 0 {\n\t\t\tif i%7 == 0 {\n\t\t\t\tb.WriteString(\",\\n\")\n\t\t\t} else {\n\t\t\t\tb.WriteString(\", \")\n\t\t\t}\n\t\t}\n\t\tb.WriteString(alg)\n\t}\n\treturn b.String()\n}\n\ntype ArgList map[string]string\n\nfunc (l ArgList) String() string {\n\tdata, _ := json.Marshal(l)\n\treturn string(data)\n}\n\nfunc (l ArgList) Set(arg string) error {\n\tparts := strings.SplitN(arg, \"=\", 2)\n\tif len(parts) != 2 {\n\t\treturn fmt.Errorf(\"invalid argument '%v'.  Must use format 'key=value'. %v\", arg, parts)\n\t}\n\tl[parts[0]] = parts[1]\n\treturn nil\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html\n//\n// See README.md for more info.\npackage jwt\n"
  },
  {
    "path": "ecdsa.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"math/big\"\n)\n\nvar (\n\t// Sadly this is missing from crypto/ecdsa compared to crypto/rsa\n\tErrECDSAVerification = errors.New(\"crypto/ecdsa: verification error\")\n)\n\n// SigningMethodECDSA implements the ECDSA family of signing methods.\n// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification\ntype SigningMethodECDSA struct {\n\tName      string\n\tHash      crypto.Hash\n\tKeySize   int\n\tCurveBits int\n}\n\n// Specific instances for EC256 and company\nvar (\n\tSigningMethodES256 *SigningMethodECDSA\n\tSigningMethodES384 *SigningMethodECDSA\n\tSigningMethodES512 *SigningMethodECDSA\n)\n\nfunc init() {\n\t// ES256\n\tSigningMethodES256 = &SigningMethodECDSA{\"ES256\", crypto.SHA256, 32, 256}\n\tRegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {\n\t\treturn SigningMethodES256\n\t})\n\n\t// ES384\n\tSigningMethodES384 = &SigningMethodECDSA{\"ES384\", crypto.SHA384, 48, 384}\n\tRegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {\n\t\treturn SigningMethodES384\n\t})\n\n\t// ES512\n\tSigningMethodES512 = &SigningMethodECDSA{\"ES512\", crypto.SHA512, 66, 521}\n\tRegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {\n\t\treturn SigningMethodES512\n\t})\n}\n\nfunc (m *SigningMethodECDSA) Alg() string {\n\treturn m.Name\n}\n\n// Verify implements token verification for the SigningMethod.\n// For this verify method, key must be an ecdsa.PublicKey struct\nfunc (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key any) error {\n\t// Get the key\n\tvar ecdsaKey *ecdsa.PublicKey\n\tswitch k := key.(type) {\n\tcase *ecdsa.PublicKey:\n\t\tecdsaKey = k\n\tdefault:\n\t\treturn newError(\"ECDSA verify expects *ecdsa.PublicKey\", ErrInvalidKeyType)\n\t}\n\n\tif len(sig) != 2*m.KeySize {\n\t\treturn ErrECDSAVerification\n\t}\n\n\tr := big.NewInt(0).SetBytes(sig[:m.KeySize])\n\ts := big.NewInt(0).SetBytes(sig[m.KeySize:])\n\n\t// Create hasher\n\tif !m.Hash.Available() {\n\t\treturn ErrHashUnavailable\n\t}\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\t// Verify the signature\n\tif verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus {\n\t\treturn nil\n\t}\n\n\treturn ErrECDSAVerification\n}\n\n// Sign implements token signing for the SigningMethod.\n// For this signing method, key must be an ecdsa.PrivateKey struct\nfunc (m *SigningMethodECDSA) Sign(signingString string, key any) ([]byte, error) {\n\t// Get the key\n\tvar ecdsaKey *ecdsa.PrivateKey\n\tswitch k := key.(type) {\n\tcase *ecdsa.PrivateKey:\n\t\tecdsaKey = k\n\tdefault:\n\t\treturn nil, newError(\"ECDSA sign expects *ecdsa.PrivateKey\", ErrInvalidKeyType)\n\t}\n\n\t// Create the hasher\n\tif !m.Hash.Available() {\n\t\treturn nil, ErrHashUnavailable\n\t}\n\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\t// Sign the string and return r, s\n\tif r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {\n\t\tcurveBits := ecdsaKey.Curve.Params().BitSize\n\n\t\tif m.CurveBits != curveBits {\n\t\t\treturn nil, ErrInvalidKey\n\t\t}\n\n\t\tkeyBytes := curveBits / 8\n\t\tif curveBits%8 > 0 {\n\t\t\tkeyBytes += 1\n\t\t}\n\n\t\t// We serialize the outputs (r and s) into big-endian byte arrays\n\t\t// padded with zeros on the left to make sure the sizes work out.\n\t\t// Output must be 2*keyBytes long.\n\t\tout := make([]byte, 2*keyBytes)\n\t\tr.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output.\n\t\ts.FillBytes(out[keyBytes:])  // s is assigned to the second half of output.\n\n\t\treturn out, nil\n\t} else {\n\t\treturn nil, err\n\t}\n}\n"
  },
  {
    "path": "ecdsa_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"crypto/ecdsa\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar ecdsaTestData = []struct {\n\tname        string\n\tkeys        map[string]string\n\ttokenString string\n\talg         string\n\tclaims      map[string]any\n\tvalid       bool\n}{\n\t{\n\t\t\"Basic ES256\",\n\t\tmap[string]string{\"private\": \"test/ec256-private.pem\", \"public\": \"test/ec256-public.pem\"},\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJmb28iOiJiYXIifQ.feG39E-bn8HXAKhzDZq7yEAPWYDhZlwTn3sePJnU9VrGMmwdXAIEyoOnrjreYlVM_Z4N13eK9-TmMTWyfKJtHQ\",\n\t\t\"ES256\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic ES384\",\n\t\tmap[string]string{\"private\": \"test/ec384-private.pem\", \"public\": \"test/ec384-public.pem\"},\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJmb28iOiJiYXIifQ.ngAfKMbJUh0WWubSIYe5GMsA-aHNKwFbJk_wq3lq23aPp8H2anb1rRILIzVR0gUf4a8WzDtrzmiikuPWyCS6CN4-PwdgTk-5nehC7JXqlaBZU05p3toM3nWCwm_LXcld\",\n\t\t\"ES384\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic ES512\",\n\t\tmap[string]string{\"private\": \"test/ec512-private.pem\", \"public\": \"test/ec512-public.pem\"},\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJmb28iOiJiYXIifQ.AAU0TvGQOcdg2OvrwY73NHKgfk26UDekh9Prz-L_iWuTBIBqOFCWwwLsRiHB1JOddfKAls5do1W0jR_F30JpVd-6AJeTjGKA4C1A1H6gIKwRY0o_tFDIydZCl_lMBMeG5VNFAjO86-WCSKwc3hqaGkq1MugPRq_qrF9AVbuEB4JPLyL5\",\n\t\t\"ES512\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"basic ES256 invalid: foo => bar\",\n\t\tmap[string]string{\"private\": \"test/ec256-private.pem\", \"public\": \"test/ec256-public.pem\"},\n\t\t\"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W\",\n\t\t\"ES256\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\tfalse,\n\t},\n}\n\nfunc TestECDSAVerify(t *testing.T) {\n\tfor _, data := range ecdsaTestData {\n\t\tvar err error\n\n\t\tkey, _ := os.ReadFile(data.keys[\"public\"])\n\n\t\tvar ecdsaKey *ecdsa.PublicKey\n\t\tif ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil {\n\t\t\tt.Errorf(\"Unable to parse ECDSA public key: %v\", err)\n\t\t}\n\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\terr = method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), ecdsaKey)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestECDSASign(t *testing.T) {\n\tfor _, data := range ecdsaTestData {\n\t\tvar err error\n\t\tkey, _ := os.ReadFile(data.keys[\"private\"])\n\n\t\tvar ecdsaKey *ecdsa.PrivateKey\n\t\tif ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil {\n\t\t\tt.Errorf(\"Unable to parse ECDSA private key: %v\", err)\n\t\t}\n\n\t\tif data.valid {\n\t\t\tparts := strings.Split(data.tokenString, \".\")\n\t\t\ttoSign := strings.Join(parts[0:2], \".\")\n\t\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\t\tsig, err := method.Sign(toSign, ecdsaKey)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t\t}\n\n\t\t\tssig := encodeSegment(sig)\n\t\t\tif ssig == parts[2] {\n\t\t\t\tt.Errorf(\"[%v] Identical signatures\\nbefore:\\n%v\\nafter:\\n%v\", data.name, parts[2], ssig)\n\t\t\t}\n\n\t\t\terr = method.Verify(toSign, sig, ecdsaKey.Public())\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"[%v] Sign produced an invalid signature: %v\", data.name, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkECDSAParsing(b *testing.B) {\n\tfor _, data := range ecdsaTestData {\n\t\tkey, _ := os.ReadFile(data.keys[\"private\"])\n\n\t\tb.Run(data.name, func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tif _, err := jwt.ParseECPrivateKeyFromPEM(key); err != nil {\n\t\t\t\t\t\tb.Fatalf(\"Unable to parse ECDSA private key: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc BenchmarkECDSASigning(b *testing.B) {\n\tfor _, data := range ecdsaTestData {\n\t\tkey, _ := os.ReadFile(data.keys[\"private\"])\n\n\t\tecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(key)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Unable to parse ECDSA private key: %v\", err)\n\t\t}\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\n\t\tb.Run(data.name, func(b *testing.B) {\n\t\t\tbenchmarkSigning(b, method, ecdsaKey)\n\t\t})\n\n\t\t// Directly call method.Sign without the decoration of *Token.\n\t\tb.Run(data.name+\"/sign-only\", func(b *testing.B) {\n\t\t\tif !data.valid {\n\t\t\t\tb.Skipf(\"Skipping because data is not valid\")\n\t\t\t}\n\n\t\t\tparts := strings.Split(data.tokenString, \".\")\n\t\t\ttoSign := strings.Join(parts[0:2], \".\")\n\n\t\t\tb.ReportAllocs()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tsig, err := method.Sign(toSign, ecdsaKey)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatalf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t\t\t}\n\t\t\t\tif reflect.DeepEqual(sig, decodeSegment(b, parts[2])) {\n\t\t\t\t\tb.Fatalf(\"[%v] Identical signatures\\nbefore:\\n%v\\nafter:\\n%v\", data.name, parts[2], sig)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc decodeSegment(t interface{ Fatalf(string, ...any) }, signature string) (sig []byte) {\n\tvar err error\n\tsig, err = jwt.NewParser().DecodeSegment(signature)\n\tif err != nil {\n\t\tt.Fatalf(\"could not decode segment: %v\", err)\n\t}\n\n\treturn\n}\n\nfunc encodeSegment(sig []byte) string {\n\treturn (&jwt.Token{}).EncodeSegment(sig)\n}\n"
  },
  {
    "path": "ecdsa_utils.go",
    "content": "package jwt\n\nimport (\n\t\"crypto/ecdsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n)\n\nvar (\n\tErrNotECPublicKey  = errors.New(\"key is not a valid ECDSA public key\")\n\tErrNotECPrivateKey = errors.New(\"key is not a valid ECDSA private key\")\n)\n\n// ParseECPrivateKeyFromPEM parses a PEM encoded Elliptic Curve Private Key Structure\nfunc ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\t// Parse the key\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {\n\t\tif parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar pkey *ecdsa.PrivateKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {\n\t\treturn nil, ErrNotECPrivateKey\n\t}\n\n\treturn pkey, nil\n}\n\n// ParseECPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key\nfunc ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\t// Parse the key\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {\n\t\tif cert, err := x509.ParseCertificate(block.Bytes); err == nil {\n\t\t\tparsedKey = cert.PublicKey\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar pkey *ecdsa.PublicKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {\n\t\treturn nil, ErrNotECPublicKey\n\t}\n\n\treturn pkey, nil\n}\n"
  },
  {
    "path": "ed25519.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/ed25519\"\n\t\"crypto/rand\"\n\t\"errors\"\n)\n\nvar (\n\tErrEd25519Verification = errors.New(\"ed25519: verification error\")\n)\n\n// SigningMethodEd25519 implements the EdDSA family.\n// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification\ntype SigningMethodEd25519 struct{}\n\n// Specific instance for EdDSA\nvar (\n\tSigningMethodEdDSA *SigningMethodEd25519\n)\n\nfunc init() {\n\tSigningMethodEdDSA = &SigningMethodEd25519{}\n\tRegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod {\n\t\treturn SigningMethodEdDSA\n\t})\n}\n\nfunc (m *SigningMethodEd25519) Alg() string {\n\treturn \"EdDSA\"\n}\n\n// Verify implements token verification for the SigningMethod.\n// For this verify method, key must be an ed25519.PublicKey\nfunc (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key any) error {\n\tvar ed25519Key ed25519.PublicKey\n\tvar ok bool\n\n\tif ed25519Key, ok = key.(ed25519.PublicKey); !ok {\n\t\treturn newError(\"Ed25519 verify expects ed25519.PublicKey\", ErrInvalidKeyType)\n\t}\n\n\tif len(ed25519Key) != ed25519.PublicKeySize {\n\t\treturn ErrInvalidKey\n\t}\n\n\t// Verify the signature\n\tif !ed25519.Verify(ed25519Key, []byte(signingString), sig) {\n\t\treturn ErrEd25519Verification\n\t}\n\n\treturn nil\n}\n\n// Sign implements token signing for the SigningMethod.\n// For this signing method, key must be an ed25519.PrivateKey\nfunc (m *SigningMethodEd25519) Sign(signingString string, key any) ([]byte, error) {\n\tvar ed25519Key crypto.Signer\n\tvar ok bool\n\n\tif ed25519Key, ok = key.(crypto.Signer); !ok {\n\t\treturn nil, newError(\"Ed25519 sign expects crypto.Signer\", ErrInvalidKeyType)\n\t}\n\n\tif _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok {\n\t\treturn nil, ErrInvalidKey\n\t}\n\n\t// Sign the string and return the result. ed25519 performs a two-pass hash\n\t// as part of its algorithm. Therefore, we need to pass a non-prehashed\n\t// message into the Sign function, as indicated by crypto.Hash(0)\n\tsig, err := ed25519Key.Sign(rand.Reader, []byte(signingString), crypto.Hash(0))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn sig, nil\n}\n"
  },
  {
    "path": "ed25519_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar ed25519TestData = []struct {\n\tname        string\n\tkeys        map[string]string\n\ttokenString string\n\talg         string\n\tclaims      map[string]any\n\tvalid       bool\n}{\n\t{\n\t\t\"Basic Ed25519\",\n\t\tmap[string]string{\"private\": \"test/ed25519-private.pem\", \"public\": \"test/ed25519-public.pem\"},\n\t\t\"eyJhbGciOiJFRDI1NTE5IiwidHlwIjoiSldUIn0.eyJmb28iOiJiYXIifQ.ESuVzZq1cECrt9Od_gLPVG-_6uRP_8Nq-ajx6CtmlDqRJZqdejro2ilkqaQgSL-siE_3JMTUW7UwAorLaTyFCw\",\n\t\t\"EdDSA\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic Ed25519\",\n\t\tmap[string]string{\"private\": \"test/ed25519-private.pem\", \"public\": \"test/ed25519-public.pem\"},\n\t\t\"eyJhbGciOiJFRDI1NTE5IiwidHlwIjoiSldUIn0.eyJmb28iOiJiYXoifQ.ESuVzZq1cECrt9Od_gLPVG-_6uRP_8Nq-ajx6CtmlDqRJZqdejro2ilkqaQgSL-siE_3JMTUW7UwAorLaTyFCw\",\n\t\t\"EdDSA\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\tfalse,\n\t},\n}\n\nfunc TestEd25519Verify(t *testing.T) {\n\tfor _, data := range ed25519TestData {\n\t\tvar err error\n\n\t\tkey, _ := os.ReadFile(data.keys[\"public\"])\n\n\t\ted25519Key, err := jwt.ParseEdPublicKeyFromPEM(key)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unable to parse Ed25519 public key: %v\", err)\n\t\t}\n\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\n\t\terr = method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), ed25519Key)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestEd25519Sign(t *testing.T) {\n\tfor _, data := range ed25519TestData {\n\t\tvar err error\n\t\tkey, _ := os.ReadFile(data.keys[\"private\"])\n\n\t\ted25519Key, err := jwt.ParseEdPrivateKeyFromPEM(key)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unable to parse Ed25519 private key: %v\", err)\n\t\t}\n\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\n\t\tsig, err := method.Sign(strings.Join(parts[0:2], \".\"), ed25519Key)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t}\n\n\t\tssig := encodeSegment(sig)\n\t\tif ssig == parts[2] && !data.valid {\n\t\t\tt.Errorf(\"[%v] Identical signatures\\nbefore:\\n%v\\nafter:\\n%v\", data.name, parts[2], ssig)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ed25519_utils.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/ed25519\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n)\n\nvar (\n\tErrNotEdPrivateKey = errors.New(\"key is not a valid Ed25519 private key\")\n\tErrNotEdPublicKey  = errors.New(\"key is not a valid Ed25519 public key\")\n)\n\n// ParseEdPrivateKeyFromPEM parses a PEM-encoded Edwards curve private key\nfunc ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\t// Parse the key\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar pkey ed25519.PrivateKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(ed25519.PrivateKey); !ok {\n\t\treturn nil, ErrNotEdPrivateKey\n\t}\n\n\treturn pkey, nil\n}\n\n// ParseEdPublicKeyFromPEM parses a PEM-encoded Edwards curve public key\nfunc ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\t// Parse the key\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar pkey ed25519.PublicKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(ed25519.PublicKey); !ok {\n\t\treturn nil, ErrNotEdPublicKey\n\t}\n\n\treturn pkey, nil\n}\n"
  },
  {
    "path": "errors.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar (\n\tErrInvalidKey                = errors.New(\"key is invalid\")\n\tErrInvalidKeyType            = errors.New(\"key is of invalid type\")\n\tErrHashUnavailable           = errors.New(\"the requested hash function is unavailable\")\n\tErrTokenMalformed            = errors.New(\"token is malformed\")\n\tErrTokenUnverifiable         = errors.New(\"token is unverifiable\")\n\tErrTokenSignatureInvalid     = errors.New(\"token signature is invalid\")\n\tErrTokenRequiredClaimMissing = errors.New(\"token is missing required claim\")\n\tErrTokenInvalidAudience      = errors.New(\"token has invalid audience\")\n\tErrTokenExpired              = errors.New(\"token is expired\")\n\tErrTokenUsedBeforeIssued     = errors.New(\"token used before issued\")\n\tErrTokenInvalidIssuer        = errors.New(\"token has invalid issuer\")\n\tErrTokenInvalidSubject       = errors.New(\"token has invalid subject\")\n\tErrTokenNotValidYet          = errors.New(\"token is not valid yet\")\n\tErrTokenInvalidId            = errors.New(\"token has invalid id\")\n\tErrTokenInvalidClaims        = errors.New(\"token has invalid claims\")\n\tErrInvalidType               = errors.New(\"invalid type for claim\")\n)\n\n// joinedError is an error type that works similar to what [errors.Join]\n// produces, with the exception that it has a nice error string; mainly its\n// error messages are concatenated using a comma, rather than a newline.\ntype joinedError struct {\n\terrs []error\n}\n\nfunc (je joinedError) Error() string {\n\tmsg := []string{}\n\tfor _, err := range je.errs {\n\t\tmsg = append(msg, err.Error())\n\t}\n\n\treturn strings.Join(msg, \", \")\n}\n\n// joinErrors joins together multiple errors. Useful for scenarios where\n// multiple errors next to each other occur, e.g., in claims validation.\nfunc joinErrors(errs ...error) error {\n\treturn &joinedError{\n\t\terrs: errs,\n\t}\n}\n\n// Unwrap implements the multiple error unwrapping for this error type, which is\n// possible in Go 1.20.\nfunc (je joinedError) Unwrap() []error {\n\treturn je.errs\n}\n\n// newError creates a new error message with a detailed error message. The\n// message will be prefixed with the contents of the supplied error type.\n// Additionally, more errors, that provide more context can be supplied which\n// will be appended to the message. This makes use of Go 1.20's possibility to\n// include more than one %w formatting directive in [fmt.Errorf].\n//\n// For example,\n//\n//\tnewError(\"no keyfunc was provided\", ErrTokenUnverifiable)\n//\n// will produce the error string\n//\n//\t\"token is unverifiable: no keyfunc was provided\"\nfunc newError(message string, err error, more ...error) error {\n\tvar format string\n\tvar args []any\n\tif message != \"\" {\n\t\tformat = \"%w: %s\"\n\t\targs = []any{err, message}\n\t} else {\n\t\tformat = \"%w\"\n\t\targs = []any{err}\n\t}\n\n\tfor _, e := range more {\n\t\tformat += \": %w\"\n\t\targs = append(args, e)\n\t}\n\n\terr = fmt.Errorf(format, args...)\n\treturn err\n}\n"
  },
  {
    "path": "errors_test.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc Test_joinErrors(t *testing.T) {\n\ttype args struct {\n\t\terrs []error\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\targs        args\n\t\twantErrors  []error\n\t\twantMessage string\n\t}{\n\t\t{\n\t\t\tname: \"multiple errors\",\n\t\t\targs: args{\n\t\t\t\terrs: []error{ErrTokenNotValidYet, ErrTokenExpired},\n\t\t\t},\n\t\t\twantErrors:  []error{ErrTokenNotValidYet, ErrTokenExpired},\n\t\t\twantMessage: \"token is not valid yet, token is expired\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := joinErrors(tt.args.errs...)\n\t\t\tfor _, wantErr := range tt.wantErrors {\n\t\t\t\tif !errors.Is(err, wantErr) {\n\t\t\t\t\tt.Errorf(\"joinErrors() error = %v, does not contain %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif err.Error() != tt.wantMessage {\n\t\t\t\tt.Errorf(\"joinErrors() error.Error() = %v, wantMessage %v\", err, tt.wantMessage)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_newError(t *testing.T) {\n\ttype args struct {\n\t\tmessage string\n\t\terr     error\n\t\tmore    []error\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\targs        args\n\t\twantErrors  []error\n\t\twantMessage string\n\t}{\n\t\t{\n\t\t\tname:        \"single error\",\n\t\t\targs:        args{message: \"something is wrong\", err: ErrTokenMalformed},\n\t\t\twantMessage: \"token is malformed: something is wrong\",\n\t\t\twantErrors:  []error{ErrTokenMalformed},\n\t\t},\n\t\t{\n\t\t\tname:        \"two errors\",\n\t\t\targs:        args{message: \"something is wrong\", err: ErrTokenMalformed, more: []error{io.ErrUnexpectedEOF}},\n\t\t\twantMessage: \"token is malformed: something is wrong: unexpected EOF\",\n\t\t\twantErrors:  []error{ErrTokenMalformed},\n\t\t},\n\t\t{\n\t\t\tname:        \"two errors, no detail\",\n\t\t\targs:        args{message: \"\", err: ErrTokenInvalidClaims, more: []error{ErrTokenExpired}},\n\t\t\twantMessage: \"token has invalid claims: token is expired\",\n\t\t\twantErrors:  []error{ErrTokenInvalidClaims, ErrTokenExpired},\n\t\t},\n\t\t{\n\t\t\tname:        \"two errors, no detail and join error\",\n\t\t\targs:        args{message: \"\", err: ErrTokenInvalidClaims, more: []error{joinErrors(ErrTokenExpired, ErrTokenNotValidYet)}},\n\t\t\twantMessage: \"token has invalid claims: token is expired, token is not valid yet\",\n\t\t\twantErrors:  []error{ErrTokenInvalidClaims, ErrTokenExpired, ErrTokenNotValidYet},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := newError(tt.args.message, tt.args.err, tt.args.more...)\n\t\t\tfor _, wantErr := range tt.wantErrors {\n\t\t\t\tif !errors.Is(err, wantErr) {\n\t\t\t\t\tt.Errorf(\"newError() error = %v, does not contain %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif err.Error() != tt.wantMessage {\n\t\t\t\tt.Errorf(\"newError() error.Error() = %v, wantMessage %v\", err, tt.wantMessage)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "example_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// Example (atypical) using the RegisteredClaims type by itself to parse a token.\n// The RegisteredClaims type is designed to be embedded into your custom types\n// to provide standard validation features.  You can use it alone, but there's\n// no way to retrieve other fields after parsing.\n// See the CustomClaimsType example for intended usage.\nfunc ExampleNewWithClaims_registeredClaims() {\n\tmySigningKey := []byte(\"AllYourBase\")\n\n\t// Create the Claims\n\tclaims := &jwt.RegisteredClaims{\n\t\tExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),\n\t\tIssuer:    \"test\",\n\t}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tss, err := token.SignedString(mySigningKey)\n\tfmt.Println(ss, err)\n\t// Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.0XN_1Tpp9FszFOonIBpwha0c_SfnNI22DhTnjMshPg8 <nil>\n}\n\n// Example creating a token using a custom claims type. The RegisteredClaims is embedded\n// in the custom type to allow for easy encoding, parsing and validation of registered claims.\nfunc ExampleNewWithClaims_customClaimsType() {\n\tmySigningKey := []byte(\"AllYourBase\")\n\n\ttype MyCustomClaims struct {\n\t\tFoo string `json:\"foo\"`\n\t\tjwt.RegisteredClaims\n\t}\n\n\t// Create claims with multiple fields populated\n\tclaims := MyCustomClaims{\n\t\t\"bar\",\n\t\tjwt.RegisteredClaims{\n\t\t\t// A usual scenario is to set the expiration time relative to the current time\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),\n\t\t\tIssuedAt:  jwt.NewNumericDate(time.Now()),\n\t\t\tNotBefore: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer:    \"test\",\n\t\t\tSubject:   \"somebody\",\n\t\t\tID:        \"1\",\n\t\t\tAudience:  []string{\"somebody_else\"},\n\t\t},\n\t}\n\n\tfmt.Printf(\"foo: %v\\n\", claims.Foo)\n\n\t// Create claims while leaving out some of the optional fields\n\tclaims = MyCustomClaims{\n\t\t\"bar\",\n\t\tjwt.RegisteredClaims{\n\t\t\t// Also fixed dates can be used for the NumericDate\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),\n\t\t\tIssuer:    \"test\",\n\t\t},\n\t}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tss, err := token.SignedString(mySigningKey)\n\tfmt.Println(ss, err)\n\n\t// Output: foo: bar\n\t// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.xVuY2FZ_MRXMIEgVQ7J-TFtaucVFRXUzHm9LmV41goM <nil>\n}\n\n// Example creating a token using a custom claims type.  The RegisteredClaims is embedded\n// in the custom type to allow for easy encoding, parsing and validation of standard claims.\nfunc ExampleParseWithClaims_customClaimsType() {\n\ttokenString := \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA\"\n\n\ttype MyCustomClaims struct {\n\t\tFoo string `json:\"foo\"`\n\t\tjwt.RegisteredClaims\n\t}\n\n\ttoken, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (any, error) {\n\t\treturn []byte(\"AllYourBase\"), nil\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if claims, ok := token.Claims.(*MyCustomClaims); ok {\n\t\tfmt.Println(claims.Foo, claims.Issuer)\n\t} else {\n\t\tlog.Fatal(\"unknown claims type, cannot proceed\")\n\t}\n\n\t// Output: bar test\n}\n\n// Example creating a token using a custom claims type and validation options.  The RegisteredClaims is embedded\n// in the custom type to allow for easy encoding, parsing and validation of standard claims.\nfunc ExampleParseWithClaims_validationOptions() {\n\ttokenString := \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA\"\n\n\ttype MyCustomClaims struct {\n\t\tFoo string `json:\"foo\"`\n\t\tjwt.RegisteredClaims\n\t}\n\n\ttoken, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (any, error) {\n\t\treturn []byte(\"AllYourBase\"), nil\n\t}, jwt.WithLeeway(5*time.Second))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if claims, ok := token.Claims.(*MyCustomClaims); ok {\n\t\tfmt.Println(claims.Foo, claims.Issuer)\n\t} else {\n\t\tlog.Fatal(\"unknown claims type, cannot proceed\")\n\t}\n\n\t// Output: bar test\n}\n\ntype MyCustomClaims struct {\n\tFoo string `json:\"foo\"`\n\tjwt.RegisteredClaims\n}\n\n// Ensure we implement [jwt.ClaimsValidator] at compile time so we know our custom Validate method is used.\nvar _ jwt.ClaimsValidator = (*MyCustomClaims)(nil)\n\n// Validate can be used to execute additional application-specific claims\n// validation.\nfunc (m MyCustomClaims) Validate() error {\n\tif m.Foo != \"bar\" {\n\t\treturn errors.New(\"must be foobar\")\n\t}\n\n\treturn nil\n}\n\n// Example creating a token using a custom claims type and validation options.\n// The RegisteredClaims is embedded in the custom type to allow for easy\n// encoding, parsing and validation of standard claims and the function\n// CustomValidation is implemented.\nfunc ExampleParseWithClaims_customValidation() {\n\ttokenString := \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA\"\n\n\ttoken, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (any, error) {\n\t\treturn []byte(\"AllYourBase\"), nil\n\t}, jwt.WithLeeway(5*time.Second))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if claims, ok := token.Claims.(*MyCustomClaims); ok {\n\t\tfmt.Println(claims.Foo, claims.Issuer)\n\t} else {\n\t\tlog.Fatal(\"unknown claims type, cannot proceed\")\n\t}\n\n\t// Output: bar test\n}\n\n// An example of parsing the error types using errors.Is.\nfunc ExampleParse_errorChecking() {\n\t// Token from another example.  This token is expired\n\tvar tokenString = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c\"\n\n\ttoken, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {\n\t\treturn []byte(\"AllYourBase\"), nil\n\t})\n\n\tswitch {\n\tcase token.Valid:\n\t\tfmt.Println(\"You look nice today\")\n\tcase errors.Is(err, jwt.ErrTokenMalformed):\n\t\tfmt.Println(\"That's not even a token\")\n\tcase errors.Is(err, jwt.ErrTokenSignatureInvalid):\n\t\t// Invalid signature\n\t\tfmt.Println(\"Invalid signature\")\n\tcase errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet):\n\t\t// Token is either expired or not active yet\n\t\tfmt.Println(\"Timing is everything\")\n\tdefault:\n\t\tfmt.Println(\"Couldn't handle this token:\", err)\n\t}\n\n\t// Output: Timing is everything\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/golang-jwt/jwt/v5\n\ngo 1.21\n"
  },
  {
    "path": "go.sum",
    "content": ""
  },
  {
    "path": "hmac.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/hmac\"\n\t\"errors\"\n)\n\n// SigningMethodHMAC implements the HMAC-SHA family of signing methods.\n// Expects key type of []byte for both signing and validation\ntype SigningMethodHMAC struct {\n\tName string\n\tHash crypto.Hash\n}\n\n// Specific instances for HS256 and company\nvar (\n\tSigningMethodHS256  *SigningMethodHMAC\n\tSigningMethodHS384  *SigningMethodHMAC\n\tSigningMethodHS512  *SigningMethodHMAC\n\tErrSignatureInvalid = errors.New(\"signature is invalid\")\n)\n\nfunc init() {\n\t// HS256\n\tSigningMethodHS256 = &SigningMethodHMAC{\"HS256\", crypto.SHA256}\n\tRegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {\n\t\treturn SigningMethodHS256\n\t})\n\n\t// HS384\n\tSigningMethodHS384 = &SigningMethodHMAC{\"HS384\", crypto.SHA384}\n\tRegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {\n\t\treturn SigningMethodHS384\n\t})\n\n\t// HS512\n\tSigningMethodHS512 = &SigningMethodHMAC{\"HS512\", crypto.SHA512}\n\tRegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {\n\t\treturn SigningMethodHS512\n\t})\n}\n\nfunc (m *SigningMethodHMAC) Alg() string {\n\treturn m.Name\n}\n\n// Verify implements token verification for the SigningMethod. Returns nil if\n// the signature is valid. Key must be []byte.\n//\n// Note it is not advised to provide a []byte which was converted from a 'human\n// readable' string using a subset of ASCII characters. To maximize entropy, you\n// should ideally be providing a []byte key which was produced from a\n// cryptographically random source, e.g. crypto/rand. Additional information\n// about this, and why we intentionally are not supporting string as a key can\n// be found on our usage guide\n// https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types.\nfunc (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key any) error {\n\t// Verify the key is the right type\n\tkeyBytes, ok := key.([]byte)\n\tif !ok {\n\t\treturn newError(\"HMAC verify expects []byte\", ErrInvalidKeyType)\n\t}\n\n\t// Can we use the specified hashing method?\n\tif !m.Hash.Available() {\n\t\treturn ErrHashUnavailable\n\t}\n\n\t// This signing method is symmetric, so we validate the signature\n\t// by reproducing the signature from the signing string and key, then\n\t// comparing that against the provided signature.\n\thasher := hmac.New(m.Hash.New, keyBytes)\n\thasher.Write([]byte(signingString))\n\tif !hmac.Equal(sig, hasher.Sum(nil)) {\n\t\treturn ErrSignatureInvalid\n\t}\n\n\t// No validation errors.  Signature is good.\n\treturn nil\n}\n\n// Sign implements token signing for the SigningMethod. Key must be []byte.\n//\n// Note it is not advised to provide a []byte which was converted from a 'human\n// readable' string using a subset of ASCII characters. To maximize entropy, you\n// should ideally be providing a []byte key which was produced from a\n// cryptographically random source, e.g. crypto/rand. Additional information\n// about this, and why we intentionally are not supporting string as a key can\n// be found on our usage guide https://golang-jwt.github.io/jwt/usage/signing_methods/.\nfunc (m *SigningMethodHMAC) Sign(signingString string, key any) ([]byte, error) {\n\tif keyBytes, ok := key.([]byte); ok {\n\t\tif !m.Hash.Available() {\n\t\t\treturn nil, ErrHashUnavailable\n\t\t}\n\n\t\thasher := hmac.New(m.Hash.New, keyBytes)\n\t\thasher.Write([]byte(signingString))\n\n\t\treturn hasher.Sum(nil), nil\n\t}\n\n\treturn nil, newError(\"HMAC sign expects []byte\", ErrInvalidKeyType)\n}\n"
  },
  {
    "path": "hmac_example_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// For HMAC signing method, the key can be any []byte. It is recommended to generate\n// a key using crypto/rand or something equivalent. You need the same key for signing\n// and validating.\nvar hmacSampleSecret []byte\n\nfunc init() {\n\t// Load sample key data\n\tif keyData, e := os.ReadFile(\"test/hmacTestKey\"); e == nil {\n\t\thmacSampleSecret = keyData\n\t} else {\n\t\tpanic(e)\n\t}\n}\n\n// Example creating, signing, and encoding a JWT token using the HMAC signing method\nfunc ExampleNewWithClaims_hmac() {\n\t// Create a new token object, specifying signing method and the claims\n\t// you would like it to contain.\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{\n\t\t\"foo\": \"bar\",\n\t\t\"nbf\": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),\n\t})\n\t// Sign and get the complete encoded token as a string using the secret\n\ttokenString, err := token.SignedString(hmacSampleSecret)\n\n\tfmt.Println(tokenString, err)\n\t// Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU <nil>\n}\n\n// Example parsing and validating a token using the HMAC signing method\nfunc ExampleParse_hmac() {\n\t// sample token string taken from the New example\n\ttokenString := \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU\"\n\n\t// Parse takes the token string and a function for looking up the key. The latter is especially\n\t// useful if you use multiple keys for your application.  The standard is to use 'kid' in the\n\t// head of the token to identify which key to use, but the parsed token (head and claims) is provided\n\t// to the callback, providing flexibility.\n\ttoken, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {\n\t\t// hmacSampleSecret is a []byte containing your secret, e.g. []byte(\"my_secret_key\")\n\t\treturn hmacSampleSecret, nil\n\t}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tif claims, ok := token.Claims.(jwt.MapClaims); ok {\n\t\tfmt.Println(claims[\"foo\"], claims[\"nbf\"])\n\t} else {\n\t\tfmt.Println(err)\n\t}\n\n\t// Output: bar 1.4444784e+09\n}\n"
  },
  {
    "path": "hmac_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar hmacTestData = []struct {\n\tname        string\n\ttokenString string\n\talg         string\n\tclaims      map[string]any\n\tvalid       bool\n}{\n\t{\n\t\t\"web sample\",\n\t\t\"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\",\n\t\t\"HS256\",\n\t\tmap[string]any{\"iss\": \"joe\", \"exp\": 1300819380, \"http://example.com/is_root\": true},\n\t\ttrue,\n\t},\n\t{\n\t\t\"HS384\",\n\t\t\"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.KWZEuOD5lbBxZ34g7F-SlVLAQ_r5KApWNWlZIIMyQVz5Zs58a7XdNzj5_0EcNoOy\",\n\t\t\"HS384\",\n\t\tmap[string]any{\"iss\": \"joe\", \"exp\": 1300819380, \"http://example.com/is_root\": true},\n\t\ttrue,\n\t},\n\t{\n\t\t\"HS512\",\n\t\t\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.CN7YijRX6Aw1n2jyI2Id1w90ja-DEMYiWixhYCyHnrZ1VfJRaFQz1bEbjjA5Fn4CLYaUG432dEYmSbS4Saokmw\",\n\t\t\"HS512\",\n\t\tmap[string]any{\"iss\": \"joe\", \"exp\": 1300819380, \"http://example.com/is_root\": true},\n\t\ttrue,\n\t},\n\t{\n\t\t\"web sample: invalid\",\n\t\t\"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXo\",\n\t\t\"HS256\",\n\t\tmap[string]any{\"iss\": \"joe\", \"exp\": 1300819380, \"http://example.com/is_root\": true},\n\t\tfalse,\n\t},\n}\n\n// Sample data from http://tools.ietf.org/html/draft-jones-json-web-signature-04#appendix-A.1\nvar hmacTestKey, _ = os.ReadFile(\"test/hmacTestKey\")\n\nfunc TestHMACVerify(t *testing.T) {\n\tfor _, data := range hmacTestData {\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\terr := method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), hmacTestKey)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestHMACSign(t *testing.T) {\n\tfor _, data := range hmacTestData {\n\t\tif !data.valid {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Split(data.tokenString, \".\")\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\tsig, err := method.Sign(strings.Join(parts[0:2], \".\"), hmacTestKey)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t}\n\t\tif !reflect.DeepEqual(sig, decodeSegment(t, parts[2])) {\n\t\t\tt.Errorf(\"[%v] Incorrect signature.\\nwas:\\n%v\\nexpecting:\\n%v\", data.name, sig, parts[2])\n\t\t}\n\t}\n}\n\nfunc BenchmarkHS256Signing(b *testing.B) {\n\tbenchmarkSigning(b, jwt.SigningMethodHS256, hmacTestKey)\n}\n\nfunc BenchmarkHS384Signing(b *testing.B) {\n\tbenchmarkSigning(b, jwt.SigningMethodHS384, hmacTestKey)\n}\n\nfunc BenchmarkHS512Signing(b *testing.B) {\n\tbenchmarkSigning(b, jwt.SigningMethodHS512, hmacTestKey)\n}\n"
  },
  {
    "path": "http_example_test.go",
    "content": "package jwt_test\n\n// Example HTTP auth using asymmetric crypto/RSA keys\n// This is based on a (now outdated) example at https://gist.github.com/cryptix/45c33ecf0ae54828e63b\n\nimport (\n\t\"crypto/rsa\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/golang-jwt/jwt/v5/request\"\n)\n\n// location of the files used for signing and verification\nconst (\n\tprivKeyPath = \"test/sample_key\"     // openssl genrsa -out app.rsa keysize\n\tpubKeyPath  = \"test/sample_key.pub\" // openssl rsa -in app.rsa -pubout > app.rsa.pub\n)\n\nvar (\n\tverifyKey  *rsa.PublicKey\n\tsignKey    *rsa.PrivateKey\n\tserverPort int\n)\n\n// read the key files before starting http handlers\nfunc init() {\n\tsignBytes, err := os.ReadFile(privKeyPath)\n\tfatal(err)\n\n\tsignKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)\n\tfatal(err)\n\n\tverifyBytes, err := os.ReadFile(pubKeyPath)\n\tfatal(err)\n\n\tverifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)\n\tfatal(err)\n\n\thttp.HandleFunc(\"/authenticate\", authHandler)\n\thttp.HandleFunc(\"/restricted\", restrictedHandler)\n\n\t// Setup listener\n\tlistener, err := net.ListenTCP(\"tcp\", &net.TCPAddr{})\n\tfatal(err)\n\tserverPort = listener.Addr().(*net.TCPAddr).Port\n\n\tlog.Println(\"Listening...\")\n\tgo func() {\n\t\tfatal(http.Serve(listener, nil))\n\t}()\n}\n\nfunc fatal(err error) {\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\n// Define some custom types were going to use within our tokens\ntype CustomerInfo struct {\n\tName string\n\tKind string\n}\n\ntype CustomClaimsExample struct {\n\tjwt.RegisteredClaims\n\tTokenType string\n\tCustomerInfo\n}\n\nfunc Example_getTokenViaHTTP() {\n\t// See func authHandler for an example auth handler that produces a token\n\tres, err := http.PostForm(fmt.Sprintf(\"http://localhost:%v/authenticate\", serverPort), url.Values{\n\t\t\"user\": {\"test\"},\n\t\t\"pass\": {\"known\"},\n\t})\n\tfatal(err)\n\n\tif res.StatusCode != 200 {\n\t\tfmt.Println(\"Unexpected status code\", res.StatusCode)\n\t}\n\n\t// Read the token out of the response body\n\tbuf, err := io.ReadAll(res.Body)\n\tfatal(err)\n\t_ = res.Body.Close()\n\ttokenString := strings.TrimSpace(string(buf))\n\n\t// Parse the token\n\ttoken, err := jwt.ParseWithClaims(tokenString, &CustomClaimsExample{}, func(token *jwt.Token) (any, error) {\n\t\t// since we only use the one private key to sign the tokens,\n\t\t// we also only use its public counter part to verify\n\t\treturn verifyKey, nil\n\t})\n\tfatal(err)\n\n\tclaims := token.Claims.(*CustomClaimsExample)\n\tfmt.Println(claims.Name)\n\n\t// Output: test\n}\n\nfunc Example_useTokenViaHTTP() {\n\t// Make a sample token\n\t// In a real world situation, this token will have been acquired from\n\t// some other API call (see Example_getTokenViaHTTP)\n\ttoken, err := createToken(\"foo\")\n\tfatal(err)\n\n\t// Make request.  See func restrictedHandler for example request processor\n\treq, err := http.NewRequest(\"GET\", fmt.Sprintf(\"http://localhost:%v/restricted\", serverPort), nil)\n\tfatal(err)\n\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %v\", token))\n\tres, err := http.DefaultClient.Do(req)\n\tfatal(err)\n\n\t// Read the response body\n\tbuf, err := io.ReadAll(res.Body)\n\tfatal(err)\n\t_ = res.Body.Close()\n\tfmt.Printf(\"%s\", buf)\n\n\t// Output: Welcome, foo\n}\n\nfunc createToken(user string) (string, error) {\n\t// create a signer for rsa 256\n\tt := jwt.New(jwt.GetSigningMethod(\"RS256\"))\n\n\t// set our claims\n\tt.Claims = &CustomClaimsExample{\n\t\tjwt.RegisteredClaims{\n\t\t\t// set the expire time\n\t\t\t// see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 1)),\n\t\t},\n\t\t\"level1\",\n\t\tCustomerInfo{user, \"human\"},\n\t}\n\n\t// Creat token string\n\treturn t.SignedString(signKey)\n}\n\n// reads the form values, checks them and creates the token\nfunc authHandler(w http.ResponseWriter, r *http.Request) {\n\t// make sure its post\n\tif r.Method != \"POST\" {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\t_, _ = fmt.Fprintln(w, \"No POST\", r.Method)\n\t\treturn\n\t}\n\n\tuser := r.FormValue(\"user\")\n\tpass := r.FormValue(\"pass\")\n\n\tlog.Printf(\"Authenticate: user[%s] pass[%s]\\n\", user, pass)\n\n\t// check values\n\tif user != \"test\" || pass != \"known\" {\n\t\tw.WriteHeader(http.StatusForbidden)\n\t\t_, _ = fmt.Fprintln(w, \"Wrong info\")\n\t\treturn\n\t}\n\n\ttokenString, err := createToken(user)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t_, _ = fmt.Fprintln(w, \"Sorry, error while Signing Token!\")\n\t\tlog.Printf(\"Token Signing error: %v\\n\", err)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/jwt\")\n\tw.WriteHeader(http.StatusOK)\n\t_, _ = fmt.Fprintln(w, tokenString)\n}\n\n// only accessible with a valid token\nfunc restrictedHandler(w http.ResponseWriter, r *http.Request) {\n\t// Get token from request\n\ttoken, err := request.ParseFromRequest(r, request.OAuth2Extractor, func(token *jwt.Token) (any, error) {\n\t\t// since we only use the one private key to sign the tokens,\n\t\t// we also only use its public counter part to verify\n\t\treturn verifyKey, nil\n\t}, request.WithClaims(&CustomClaimsExample{}))\n\n\t// If the token is missing or invalid, return error\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t_, _ = fmt.Fprintln(w, \"Invalid token:\", err)\n\t\treturn\n\t}\n\n\t// Token is valid\n\t_, _ = fmt.Fprintln(w, \"Welcome,\", token.Claims.(*CustomClaimsExample).Name)\n}\n"
  },
  {
    "path": "jwt_test.go",
    "content": "package jwt\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSplitToken(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname     string\n\t\tinput    string\n\t\texpected []string\n\t\tisValid  bool\n\t}{\n\t\t{\n\t\t\tname:     \"valid token with three parts\",\n\t\t\tinput:    \"header.claims.signature\",\n\t\t\texpected: []string{\"header\", \"claims\", \"signature\"},\n\t\t\tisValid:  true,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid token with two parts only\",\n\t\t\tinput:    \"header.claims\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid token with one part only\",\n\t\t\tinput:    \"header\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid token with extra delimiter\",\n\t\t\tinput:    \"header.claims.signature.extra\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid empty token\",\n\t\t\tinput:    \"\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t\t{\n\t\t\tname:     \"valid token with empty parts\",\n\t\t\tinput:    \"..signature\",\n\t\t\texpected: []string{\"\", \"\", \"signature\"},\n\t\t\tisValid:  true,\n\t\t},\n\t\t{\n\t\t\t// We are just splitting the token into parts, so we don't care about the actual values.\n\t\t\t// It is up to the caller to validate the parts.\n\t\t\tname:     \"valid token with all parts empty\",\n\t\t\tinput:    \"..\",\n\t\t\texpected: []string{\"\", \"\", \"\"},\n\t\t\tisValid:  true,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid token with just delimiters and extra part\",\n\t\t\tinput:    \"...\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid token with many delimiters\",\n\t\t\tinput:    \"header.claims.signature..................\",\n\t\t\texpected: nil,\n\t\t\tisValid:  false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tparts, ok := splitToken(tt.input)\n\t\t\tif ok != tt.isValid {\n\t\t\t\tt.Errorf(\"expected %t, got %t\", tt.isValid, ok)\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\tfor i, part := range tt.expected {\n\t\t\t\t\tif parts[i] != part {\n\t\t\t\t\t\tt.Errorf(\"expected %s, got %s\", part, parts[i])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "map_claims.go",
    "content": "package jwt\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// MapClaims is a claims type that uses the map[string]any for JSON\n// decoding. This is the default claims type if you don't supply one\ntype MapClaims map[string]any\n\n// GetExpirationTime implements the Claims interface.\nfunc (m MapClaims) GetExpirationTime() (*NumericDate, error) {\n\treturn m.parseNumericDate(\"exp\")\n}\n\n// GetNotBefore implements the Claims interface.\nfunc (m MapClaims) GetNotBefore() (*NumericDate, error) {\n\treturn m.parseNumericDate(\"nbf\")\n}\n\n// GetIssuedAt implements the Claims interface.\nfunc (m MapClaims) GetIssuedAt() (*NumericDate, error) {\n\treturn m.parseNumericDate(\"iat\")\n}\n\n// GetAudience implements the Claims interface.\nfunc (m MapClaims) GetAudience() (ClaimStrings, error) {\n\treturn m.parseClaimsString(\"aud\")\n}\n\n// GetIssuer implements the Claims interface.\nfunc (m MapClaims) GetIssuer() (string, error) {\n\treturn m.parseString(\"iss\")\n}\n\n// GetSubject implements the Claims interface.\nfunc (m MapClaims) GetSubject() (string, error) {\n\treturn m.parseString(\"sub\")\n}\n\n// parseNumericDate tries to parse a key in the map claims type as a number\n// date. This will succeed, if the underlying type is either a [float64] or a\n// [json.Number]. Otherwise, nil will be returned.\nfunc (m MapClaims) parseNumericDate(key string) (*NumericDate, error) {\n\tv, ok := m[key]\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tswitch exp := v.(type) {\n\tcase float64:\n\t\tif exp == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\treturn newNumericDateFromSeconds(exp), nil\n\tcase json.Number:\n\t\tv, _ := exp.Float64()\n\n\t\treturn newNumericDateFromSeconds(v), nil\n\t}\n\n\treturn nil, newError(fmt.Sprintf(\"%s is invalid\", key), ErrInvalidType)\n}\n\n// parseClaimsString tries to parse a key in the map claims type as a\n// [ClaimsStrings] type, which can either be a string or an array of string.\nfunc (m MapClaims) parseClaimsString(key string) (ClaimStrings, error) {\n\tvar cs []string\n\tswitch v := m[key].(type) {\n\tcase string:\n\t\tcs = append(cs, v)\n\tcase []string:\n\t\tcs = v\n\tcase []any:\n\t\tfor _, a := range v {\n\t\t\tvs, ok := a.(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, newError(fmt.Sprintf(\"%s is invalid\", key), ErrInvalidType)\n\t\t\t}\n\t\t\tcs = append(cs, vs)\n\t\t}\n\t}\n\n\treturn cs, nil\n}\n\n// parseString tries to parse a key in the map claims type as a [string] type.\n// If the key does not exist, an empty string is returned. If the key has the\n// wrong type, an error is returned.\nfunc (m MapClaims) parseString(key string) (string, error) {\n\tvar (\n\t\tok  bool\n\t\traw any\n\t\tiss string\n\t)\n\traw, ok = m[key]\n\tif !ok {\n\t\treturn \"\", nil\n\t}\n\n\tiss, ok = raw.(string)\n\tif !ok {\n\t\treturn \"\", newError(fmt.Sprintf(\"%s is invalid\", key), ErrInvalidType)\n\t}\n\n\treturn iss, nil\n}\n"
  },
  {
    "path": "map_claims_test.go",
    "content": "package jwt\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestVerifyAud(t *testing.T) {\n\tvar nilInterface any\n\tvar nilListInterface []any\n\tvar intListInterface any = []int{1, 2, 3}\n\ttype test struct {\n\t\tName        string\n\t\tMapClaims   MapClaims\n\t\tExpected    bool\n\t\tComparison  []string\n\t\tMatchAllAud bool\n\t\tRequired    bool\n\t}\n\ttests := []test{\n\t\t// Matching Claim in aud\n\t\t// Required = true\n\t\t{Name: \"String Aud matching required\", MapClaims: MapClaims{\"aud\": \"example.com\"}, Expected: true, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]String Aud with match required\", MapClaims: MapClaims{\"aud\": []string{\"example.com\", \"example.example.com\"}}, Expected: true, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]String Aud with []match any required\", MapClaims: MapClaims{\"aud\": []string{\"example.com\", \"example.example.com\"}}, Expected: true, Required: true, Comparison: []string{\"example.com\", \"auth.example.com\"}},\n\t\t{Name: \"[]String Aud with []match all required\", MapClaims: MapClaims{\"aud\": []string{\"example.com\", \"example.example.com\"}}, Expected: true, Required: true, Comparison: []string{\"example.com\", \"example.example.com\"}, MatchAllAud: true},\n\n\t\t// Required = false\n\t\t{Name: \"String Aud with match not required\", MapClaims: MapClaims{\"aud\": \"example.com\"}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty String Aud with match not required\", MapClaims: MapClaims{}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty String Aud with match not required\", MapClaims: MapClaims{\"aud\": \"\"}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Nil String Aud with match not required\", MapClaims: MapClaims{\"aud\": nil}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\n\t\t{Name: \"[]String Aud with match not required\", MapClaims: MapClaims{\"aud\": []string{\"example.com\", \"example.example.com\"}}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty []String Aud with match not required\", MapClaims: MapClaims{\"aud\": []string{}}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\n\t\t// Non-Matching Claim in aud\n\t\t// Required = true\n\t\t{Name: \"String Aud without match required\", MapClaims: MapClaims{\"aud\": \"not.example.com\"}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty String Aud without match required\", MapClaims: MapClaims{\"aud\": \"\"}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]String Aud without match required\", MapClaims: MapClaims{\"aud\": []string{\"not.example.com\", \"example.example.com\"}}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty []String Aud without match required\", MapClaims: MapClaims{\"aud\": []string{\"\"}}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"String Aud without match not required\", MapClaims: MapClaims{\"aud\": \"not.example.com\"}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"Empty String Aud without match not required\", MapClaims: MapClaims{\"aud\": \"\"}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]String Aud without match not required\", MapClaims: MapClaims{\"aud\": []string{\"not.example.com\", \"example.example.com\"}}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\n\t\t// Required = false\n\t\t{Name: \"Empty []String Aud without match required\", MapClaims: MapClaims{\"aud\": []string{\"\"}}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\n\t\t// []any\n\t\t{Name: \"Empty []interface{} Aud without match required\", MapClaims: MapClaims{\"aud\": nilListInterface}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]interface{} Aud with match required\", MapClaims: MapClaims{\"aud\": []any{\"a\", \"foo\", \"example.com\"}}, Expected: true, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]interface{} Aud with match but invalid types\", MapClaims: MapClaims{\"aud\": []any{\"a\", 5, \"example.com\"}}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\t\t{Name: \"[]interface{} Aud int with match required\", MapClaims: MapClaims{\"aud\": intListInterface}, Expected: false, Required: true, Comparison: []string{\"example.com\"}},\n\n\t\t// any\n\t\t{Name: \"Empty interface{} Aud without match not required\", MapClaims: MapClaims{\"aud\": nilInterface}, Expected: true, Required: false, Comparison: []string{\"example.com\"}},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tvar opts []ParserOption\n\n\t\t\tif test.Required && test.MatchAllAud {\n\t\t\t\topts = append(opts, WithAllAudiences(test.Comparison...))\n\t\t\t} else if test.Required {\n\t\t\t\topts = append(opts, WithAudience(test.Comparison...))\n\t\t\t}\n\n\t\t\tvalidator := NewValidator(opts...)\n\t\t\tgot := validator.Validate(test.MapClaims)\n\n\t\t\tif (got == nil) != test.Expected {\n\t\t\t\tt.Errorf(\"Expected %v, got %v\", test.Expected, (got == nil))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMapclaimsVerifyIssuedAtInvalidTypeString(t *testing.T) {\n\tmapClaims := MapClaims{\n\t\t\"iat\": \"foo\",\n\t}\n\twant := false\n\tgot := NewValidator(WithIssuedAt()).Validate(mapClaims)\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n}\n\nfunc TestMapclaimsVerifyNotBeforeInvalidTypeString(t *testing.T) {\n\tmapClaims := MapClaims{\n\t\t\"nbf\": \"foo\",\n\t}\n\twant := false\n\tgot := NewValidator().Validate(mapClaims)\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n}\n\nfunc TestMapclaimsVerifyExpiresAtInvalidTypeString(t *testing.T) {\n\tmapClaims := MapClaims{\n\t\t\"exp\": \"foo\",\n\t}\n\twant := false\n\tgot := NewValidator().Validate(mapClaims)\n\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n}\n\nfunc TestMapClaimsVerifyExpiresAtExpire(t *testing.T) {\n\texp := time.Now()\n\tmapClaims := MapClaims{\n\t\t\"exp\": float64(exp.Unix()),\n\t}\n\twant := false\n\tgot := NewValidator(WithTimeFunc(func() time.Time {\n\t\treturn exp\n\t})).Validate(mapClaims)\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n\n\tgot = NewValidator(WithTimeFunc(func() time.Time {\n\t\treturn exp.Add(1 * time.Second)\n\t})).Validate(mapClaims)\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n\n\twant = true\n\tgot = NewValidator(WithTimeFunc(func() time.Time {\n\t\treturn exp.Add(-1 * time.Second)\n\t})).Validate(mapClaims)\n\tif want != (got == nil) {\n\t\tt.Fatalf(\"Failed to verify claims, wanted: %v got %v\", want, (got == nil))\n\t}\n}\n\nfunc TestMapClaims_parseString(t *testing.T) {\n\ttype args struct {\n\t\tkey string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tm       MapClaims\n\t\targs    args\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"missing key\",\n\t\t\tm:    MapClaims{},\n\t\t\targs: args{\n\t\t\t\tkey: \"mykey\",\n\t\t\t},\n\t\t\twant:    \"\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"wrong key type\",\n\t\t\tm:    MapClaims{\"mykey\": 4},\n\t\t\targs: args{\n\t\t\t\tkey: \"mykey\",\n\t\t\t},\n\t\t\twant:    \"\",\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"correct key type\",\n\t\t\tm:    MapClaims{\"mykey\": \"mystring\"},\n\t\t\targs: args{\n\t\t\t\tkey: \"mykey\",\n\t\t\t},\n\t\t\twant:    \"mystring\",\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.m.parseString(tt.args.key)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"MapClaims.parseString() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"MapClaims.parseString() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "none.go",
    "content": "package jwt\n\n// SigningMethodNone implements the none signing method.  This is required by the spec\n// but you probably should never use it.\nvar SigningMethodNone *signingMethodNone\n\nconst UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = \"none signing method allowed\"\n\nvar NoneSignatureTypeDisallowedError error\n\ntype signingMethodNone struct{}\ntype unsafeNoneMagicConstant string\n\nfunc init() {\n\tSigningMethodNone = &signingMethodNone{}\n\tNoneSignatureTypeDisallowedError = newError(\"'none' signature type is not allowed\", ErrTokenUnverifiable)\n\n\tRegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod {\n\t\treturn SigningMethodNone\n\t})\n}\n\nfunc (m *signingMethodNone) Alg() string {\n\treturn \"none\"\n}\n\n// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key\nfunc (m *signingMethodNone) Verify(signingString string, sig []byte, key any) (err error) {\n\t// Key must be UnsafeAllowNoneSignatureType to prevent accidentally\n\t// accepting 'none' signing method\n\tif _, ok := key.(unsafeNoneMagicConstant); !ok {\n\t\treturn NoneSignatureTypeDisallowedError\n\t}\n\t// If signing method is none, signature must be an empty string\n\tif len(sig) != 0 {\n\t\treturn newError(\"'none' signing method with non-empty signature\", ErrTokenUnverifiable)\n\t}\n\n\t// Accept 'none' signing method.\n\treturn nil\n}\n\n// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key\nfunc (m *signingMethodNone) Sign(signingString string, key any) ([]byte, error) {\n\tif _, ok := key.(unsafeNoneMagicConstant); ok {\n\t\treturn []byte{}, nil\n\t}\n\n\treturn nil, NoneSignatureTypeDisallowedError\n}\n"
  },
  {
    "path": "none_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar noneTestData = []struct {\n\tname        string\n\ttokenString string\n\talg         string\n\tkey         any\n\tclaims      map[string]any\n\tvalid       bool\n}{\n\t{\n\t\t\"Basic\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.\",\n\t\t\"none\",\n\t\tjwt.UnsafeAllowNoneSignatureType,\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic - no key\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.\",\n\t\t\"none\",\n\t\tnil,\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\tfalse,\n\t},\n\t{\n\t\t\"Signed\",\n\t\t\"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw\",\n\t\t\"none\",\n\t\tjwt.UnsafeAllowNoneSignatureType,\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\tfalse,\n\t},\n}\n\nfunc TestNoneVerify(t *testing.T) {\n\tfor _, data := range noneTestData {\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\terr := method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), data.key)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestNoneSign(t *testing.T) {\n\tfor _, data := range noneTestData {\n\t\tif !data.valid {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Split(data.tokenString, \".\")\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\tsig, err := method.Sign(strings.Join(parts[0:2], \".\"), data.key)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t}\n\t\tif !reflect.DeepEqual(sig, decodeSegment(t, parts[2])) {\n\t\t\tt.Errorf(\"[%v] Incorrect signature.\\nwas:\\n%v\\nexpecting:\\n%v\", data.name, sig, parts[2])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "parser.go",
    "content": "package jwt\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nconst tokenDelimiter = \".\"\n\ntype Parser struct {\n\t// If populated, only these methods will be considered valid.\n\tvalidMethods []string\n\n\t// Use JSON Number format in JSON decoder.\n\tuseJSONNumber bool\n\n\t// Skip claims validation during token parsing.\n\tskipClaimsValidation bool\n\n\tvalidator *Validator\n\n\tdecodeStrict bool\n\n\tdecodePaddingAllowed bool\n}\n\n// NewParser creates a new Parser with the specified options\nfunc NewParser(options ...ParserOption) *Parser {\n\tp := &Parser{\n\t\tvalidator: &Validator{},\n\t}\n\n\t// Loop through our parsing options and apply them\n\tfor _, option := range options {\n\t\toption(p)\n\t}\n\n\treturn p\n}\n\n// Parse parses, validates, verifies the signature and returns the parsed token.\n// keyFunc will receive the parsed token and should return the key for validating.\nfunc (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {\n\treturn p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)\n}\n\n// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims\n// interface. This provides default values which can be overridden and allows a caller to use their own type, rather\n// than the default MapClaims implementation of Claims.\n//\n// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims),\n// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the\n// proper memory for it before passing in the overall claims, otherwise you might run into a panic.\nfunc (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {\n\ttoken, parts, err := p.ParseUnverified(tokenString, claims)\n\tif err != nil {\n\t\treturn token, err\n\t}\n\n\t// Verify signing method is in the required set\n\tif p.validMethods != nil {\n\t\tvar signingMethodValid = false\n\t\tvar alg = token.Method.Alg()\n\t\tfor _, m := range p.validMethods {\n\t\t\tif m == alg {\n\t\t\t\tsigningMethodValid = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !signingMethodValid {\n\t\t\t// signing method is not in the listed set\n\t\t\treturn token, newError(fmt.Sprintf(\"signing method %v is invalid\", alg), ErrTokenSignatureInvalid)\n\t\t}\n\t}\n\n\t// Lookup key(s)\n\tif keyFunc == nil {\n\t\t// keyFunc was not provided.  short circuiting validation\n\t\treturn token, newError(\"no keyfunc was provided\", ErrTokenUnverifiable)\n\t}\n\n\tgot, err := keyFunc(token)\n\tif err != nil {\n\t\treturn token, newError(\"error while executing keyfunc\", ErrTokenUnverifiable, err)\n\t}\n\n\t// Join together header and claims in order to verify them with the signature\n\ttext := strings.Join(parts[0:2], \".\")\n\tswitch have := got.(type) {\n\tcase VerificationKeySet:\n\t\tif len(have.Keys) == 0 {\n\t\t\treturn token, newError(\"keyfunc returned empty verification key set\", ErrTokenUnverifiable)\n\t\t}\n\n\t\t// Iterate through keys and verify signature, skipping the rest when a match is found.\n\t\t// Return the last error if no match is found.\n\t\tfor _, key := range have.Keys {\n\t\t\tif err = token.Method.Verify(text, token.Signature, key); err == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\tdefault:\n\t\terr = token.Method.Verify(text, token.Signature, have)\n\t}\n\tif err != nil {\n\t\treturn token, newError(\"\", ErrTokenSignatureInvalid, err)\n\t}\n\n\t// Validate Claims\n\tif !p.skipClaimsValidation {\n\t\t// Make sure we have at least a default validator\n\t\tif p.validator == nil {\n\t\t\tp.validator = NewValidator()\n\t\t}\n\n\t\tif err := p.validator.Validate(claims); err != nil {\n\t\t\treturn token, newError(\"\", ErrTokenInvalidClaims, err)\n\t\t}\n\t}\n\n\t// No errors so far, token is valid.\n\ttoken.Valid = true\n\n\treturn token, nil\n}\n\n// ParseUnverified parses the token but does not validate the signature.\n//\n// WARNING: Don't use this method unless you know what you're doing.\n//\n// It's only ever useful in cases where you know the signature is valid (since it has already\n// been or will be checked elsewhere in the stack) and you want to extract values from it.\nfunc (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {\n\tvar ok bool\n\tparts, ok = splitToken(tokenString)\n\tif !ok {\n\t\treturn nil, nil, newError(\"token contains an invalid number of segments\", ErrTokenMalformed)\n\t}\n\n\ttoken = &Token{Raw: tokenString}\n\n\t// Parse Header\n\tvar headerBytes []byte\n\tif headerBytes, err = p.DecodeSegment(parts[0]); err != nil {\n\t\treturn token, parts, newError(\"could not base64 decode header\", ErrTokenMalformed, err)\n\t}\n\tif err = json.Unmarshal(headerBytes, &token.Header); err != nil {\n\t\treturn token, parts, newError(\"could not JSON decode header\", ErrTokenMalformed, err)\n\t}\n\n\t// Parse Claims\n\ttoken.Claims = claims\n\n\tclaimBytes, err := p.DecodeSegment(parts[1])\n\tif err != nil {\n\t\treturn token, parts, newError(\"could not base64 decode claim\", ErrTokenMalformed, err)\n\t}\n\n\t// If `useJSONNumber` is enabled then we must use *json.Decoder to decode\n\t// the claims. However, this comes with a performance penalty so only use\n\t// it if we must and, otherwise, simple use json.Unmarshal.\n\tif !p.useJSONNumber {\n\t\t// JSON Unmarshal. Special case for map type to avoid weird pointer behavior.\n\t\tif c, ok := token.Claims.(MapClaims); ok {\n\t\t\terr = json.Unmarshal(claimBytes, &c)\n\t\t} else {\n\t\t\terr = json.Unmarshal(claimBytes, &claims)\n\t\t}\n\t} else {\n\t\tdec := json.NewDecoder(bytes.NewBuffer(claimBytes))\n\t\tdec.UseNumber()\n\t\t// JSON Decode. Special case for map type to avoid weird pointer behavior.\n\t\tif c, ok := token.Claims.(MapClaims); ok {\n\t\t\terr = dec.Decode(&c)\n\t\t} else {\n\t\t\terr = dec.Decode(&claims)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn token, parts, newError(\"could not JSON decode claim\", ErrTokenMalformed, err)\n\t}\n\n\t// Lookup signature method\n\tif method, ok := token.Header[\"alg\"].(string); ok {\n\t\tif token.Method = GetSigningMethod(method); token.Method == nil {\n\t\t\treturn token, parts, newError(\"signing method (alg) is unavailable\", ErrTokenUnverifiable)\n\t\t}\n\t} else {\n\t\treturn token, parts, newError(\"signing method (alg) is unspecified\", ErrTokenUnverifiable)\n\t}\n\n\t// Parse token signature\n\ttoken.Signature, err = p.DecodeSegment(parts[2])\n\tif err != nil {\n\t\treturn token, parts, newError(\"could not base64 decode signature\", ErrTokenMalformed, err)\n\t}\n\n\treturn token, parts, nil\n}\n\n// splitToken splits a token string into three parts: header, claims, and signature. It will only\n// return true if the token contains exactly two delimiters and three parts. In all other cases, it\n// will return nil parts and false.\nfunc splitToken(token string) ([]string, bool) {\n\tparts := make([]string, 3)\n\theader, remain, ok := strings.Cut(token, tokenDelimiter)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tparts[0] = header\n\tclaims, remain, ok := strings.Cut(remain, tokenDelimiter)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tparts[1] = claims\n\t// One more cut to ensure the signature is the last part of the token and there are no more\n\t// delimiters. This avoids an issue where malicious input could contain additional delimiters\n\t// causing unnecessary overhead parsing tokens.\n\tsignature, _, unexpected := strings.Cut(remain, tokenDelimiter)\n\tif unexpected {\n\t\treturn nil, false\n\t}\n\tparts[2] = signature\n\n\treturn parts, true\n}\n\n// DecodeSegment decodes a JWT specific base64url encoding. This function will\n// take into account whether the [Parser] is configured with additional options,\n// such as [WithStrictDecoding] or [WithPaddingAllowed].\nfunc (p *Parser) DecodeSegment(seg string) ([]byte, error) {\n\tencoding := base64.RawURLEncoding\n\n\tif p.decodePaddingAllowed {\n\t\tif l := len(seg) % 4; l > 0 {\n\t\t\tseg += strings.Repeat(\"=\", 4-l)\n\t\t}\n\t\tencoding = base64.URLEncoding\n\t}\n\n\tif p.decodeStrict {\n\t\tencoding = encoding.Strict()\n\t}\n\treturn encoding.DecodeString(seg)\n}\n\n// Parse parses, validates, verifies the signature and returns the parsed token.\n// keyFunc will receive the parsed token and should return the cryptographic key\n// for verifying the signature. The caller is strongly encouraged to set the\n// WithValidMethods option to validate the 'alg' claim in the token matches the\n// expected algorithm. For more details about the importance of validating the\n// 'alg' claim, see\n// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/\nfunc Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {\n\treturn NewParser(options...).Parse(tokenString, keyFunc)\n}\n\n// ParseWithClaims is a shortcut for NewParser().ParseWithClaims().\n//\n// Note: If you provide a custom claim implementation that embeds one of the\n// standard claims (such as RegisteredClaims), make sure that a) you either\n// embed a non-pointer version of the claims or b) if you are using a pointer,\n// allocate the proper memory for it before passing in the overall claims,\n// otherwise you might run into a panic.\nfunc ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {\n\treturn NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc)\n}\n"
  },
  {
    "path": "parser_option.go",
    "content": "package jwt\n\nimport \"time\"\n\n// ParserOption is used to implement functional-style options that modify the\n// behavior of the parser.\ntype ParserOption func(*Parser)\n\n// WithValidMethods is an option to supply algorithm methods that the parser\n// will check. Only those methods will be considered valid. It is heavily\n// encouraged to use this option in order to prevent attacks such as\n// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/.\nfunc WithValidMethods(methods []string) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validMethods = methods\n\t}\n}\n\n// WithJSONNumber is an option to configure the underlying JSON parser with\n// UseNumber.\nfunc WithJSONNumber() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.useJSONNumber = true\n\t}\n}\n\n// WithoutClaimsValidation is an option to disable claims validation. This\n// option should only be used if you exactly know what you are doing.\nfunc WithoutClaimsValidation() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.skipClaimsValidation = true\n\t}\n}\n\n// WithLeeway returns the ParserOption for specifying the leeway window.\nfunc WithLeeway(leeway time.Duration) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.leeway = leeway\n\t}\n}\n\n// WithTimeFunc returns the ParserOption for specifying the time func. The\n// primary use-case for this is testing. If you are looking for a way to account\n// for clock-skew, WithLeeway should be used instead.\nfunc WithTimeFunc(f func() time.Time) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.timeFunc = f\n\t}\n}\n\n// WithIssuedAt returns the ParserOption to enable verification\n// of issued-at.\nfunc WithIssuedAt() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.verifyIat = true\n\t}\n}\n\n// WithExpirationRequired returns the ParserOption to make exp claim required.\n// By default exp claim is optional.\nfunc WithExpirationRequired() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.requireExp = true\n\t}\n}\n\n// WithNotBeforeRequired returns the ParserOption to make nbf claim required.\n// By default nbf claim is optional.\nfunc WithNotBeforeRequired() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.requireNbf = true\n\t}\n}\n\n// WithAudience configures the validator to require any of the specified\n// audiences in the `aud` claim. Validation will fail if the audience is not\n// listed in the token or the `aud` claim is missing.\n//\n// NOTE: While the `aud` claim is OPTIONAL in a JWT, the handling of it is\n// application-specific. Since this validation API is helping developers in\n// writing secure application, we decided to REQUIRE the existence of the claim,\n// if an audience is expected.\nfunc WithAudience(aud ...string) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.expectedAud = aud\n\t}\n}\n\n// WithAllAudiences configures the validator to require all the specified\n// audiences in the `aud` claim. Validation will fail if the specified audiences\n// are not listed in the token or the `aud` claim is missing. Duplicates within\n// the list are de-duplicated since internally, we use a map to look up the\n// audiences.\n//\n// NOTE: While the `aud` claim is OPTIONAL in a JWT, the handling of it is\n// application-specific. Since this validation API is helping developers in\n// writing secure application, we decided to REQUIRE the existence of the claim,\n// if an audience is expected.\nfunc WithAllAudiences(aud ...string) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.expectedAud = aud\n\t\tp.validator.expectAllAud = true\n\t}\n}\n\n// WithIssuer configures the validator to require the specified issuer in the\n// `iss` claim. Validation will fail if a different issuer is specified in the\n// token or the `iss` claim is missing.\n//\n// NOTE: While the `iss` claim is OPTIONAL in a JWT, the handling of it is\n// application-specific. Since this validation API is helping developers in\n// writing secure application, we decided to REQUIRE the existence of the claim,\n// if an issuer is expected.\nfunc WithIssuer(iss string) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.expectedIss = iss\n\t}\n}\n\n// WithSubject configures the validator to require the specified subject in the\n// `sub` claim. Validation will fail if a different subject is specified in the\n// token or the `sub` claim is missing.\n//\n// NOTE: While the `sub` claim is OPTIONAL in a JWT, the handling of it is\n// application-specific. Since this validation API is helping developers in\n// writing secure application, we decided to REQUIRE the existence of the claim,\n// if a subject is expected.\nfunc WithSubject(sub string) ParserOption {\n\treturn func(p *Parser) {\n\t\tp.validator.expectedSub = sub\n\t}\n}\n\n// WithPaddingAllowed will enable the codec used for decoding JWTs to allow\n// padding. Note that the JWS RFC7515 states that the tokens will utilize a\n// Base64url encoding with no padding. Unfortunately, some implementations of\n// JWT are producing non-standard tokens, and thus require support for decoding.\nfunc WithPaddingAllowed() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.decodePaddingAllowed = true\n\t}\n}\n\n// WithStrictDecoding will switch the codec used for decoding JWTs into strict\n// mode. In this mode, the decoder requires that trailing padding bits are zero,\n// as described in RFC 4648 section 3.5.\nfunc WithStrictDecoding() ParserOption {\n\treturn func(p *Parser) {\n\t\tp.decodeStrict = true\n\t}\n}\n"
  },
  {
    "path": "parser_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"crypto\"\n\t\"crypto/rsa\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/golang-jwt/jwt/v5/test\"\n)\n\nvar errKeyFuncError error = fmt.Errorf(\"error loading key\")\n\n// customClaimsWithDifferentTypes embeds the standard jwt.RegisteredClaims\n// struct, along with other valid JSON Data Types\ntype customClaimsWithDifferentTypes struct {\n\tStringClaim     string       `json:\"stringClaim\"`\n\tIntClaim        int          `json:\"intClaim\"`\n\tBoolClaim       bool         `json:\"boolClaim\"`\n\tUintClaim       uint         `json:\"uintClaim\"`\n\tFloatClaim      float64      `json:\"floatClaim\"`\n\tSliceClaim      []int        `json:\"sliceClaim\"`\n\tObjectClaim     CustomObject `json:\"objectClaim\"`\n\tNilPointerClaim *int         `json:\"nilPointerClaim\"`\n\n\tjwt.RegisteredClaims\n}\n\ntype CustomObject struct {\n\tProperty string `json:\"property\"`\n}\n\nvar (\n\tjwtTestDefaultKey      *rsa.PublicKey\n\tjwtTestRSAPrivateKey   *rsa.PrivateKey\n\tjwtTestEC256PublicKey  crypto.PublicKey\n\tjwtTestEC256PrivateKey crypto.PrivateKey\n\tpaddedKey              crypto.PublicKey\n\tdefaultKeyFunc         jwt.Keyfunc = func(t *jwt.Token) (any, error) { return jwtTestDefaultKey, nil }\n\tecdsaKeyFunc           jwt.Keyfunc = func(t *jwt.Token) (any, error) { return jwtTestEC256PublicKey, nil }\n\tpaddedKeyFunc          jwt.Keyfunc = func(t *jwt.Token) (any, error) { return paddedKey, nil }\n\temptyKeyFunc           jwt.Keyfunc = func(t *jwt.Token) (any, error) { return nil, nil }\n\terrorKeyFunc           jwt.Keyfunc = func(t *jwt.Token) (any, error) { return nil, errKeyFuncError }\n\tnilKeyFunc             jwt.Keyfunc = nil\n\tmultipleZeroKeyFunc    jwt.Keyfunc = func(t *jwt.Token) (any, error) { return []any{}, nil }\n\tmultipleEmptyKeyFunc   jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn jwt.VerificationKeySet{Keys: []jwt.VerificationKey{nil, nil}}, nil\n\t}\n\tmultipleVerificationKeysFunc jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn []jwt.VerificationKey{jwtTestDefaultKey, jwtTestEC256PublicKey}, nil\n\t}\n\tmultipleLastKeyFunc jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn jwt.VerificationKeySet{Keys: []jwt.VerificationKey{jwtTestEC256PublicKey, jwtTestDefaultKey}}, nil\n\t}\n\tmultipleFirstKeyFunc jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn jwt.VerificationKeySet{Keys: []jwt.VerificationKey{jwtTestDefaultKey, jwtTestEC256PublicKey}}, nil\n\t}\n\tmultipleAltTypedKeyFunc jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn jwt.VerificationKeySet{Keys: []jwt.VerificationKey{jwtTestDefaultKey, jwtTestDefaultKey}}, nil\n\t}\n\temptyVerificationKeySetFunc jwt.Keyfunc = func(t *jwt.Token) (any, error) {\n\t\treturn jwt.VerificationKeySet{}, nil\n\t}\n)\n\nfunc init() {\n\t// Load public keys\n\tjwtTestDefaultKey = test.LoadRSAPublicKeyFromDisk(\"test/sample_key.pub\")\n\tjwtTestEC256PublicKey = test.LoadECPublicKeyFromDisk(\"test/ec256-public.pem\")\n\n\t// Load padded public key - note there is only a public key for this key pair and should only be used for the\n\t// two test cases below.\n\tpaddedKey = test.LoadECPublicKeyFromDisk(\"test/examplePaddedKey-public.pem\")\n\n\t// Load private keys\n\tjwtTestRSAPrivateKey = test.LoadRSAPrivateKeyFromDisk(\"test/sample_key\")\n\tjwtTestEC256PrivateKey = test.LoadECPrivateKeyFromDisk(\"test/ec256-private.pem\")\n}\n\nvar jwtTestData = []struct {\n\tname          string\n\ttokenString   string\n\tkeyfunc       jwt.Keyfunc\n\tclaims        jwt.Claims\n\tvalid         bool\n\terr           []error\n\tparser        *jwt.Parser\n\tsigningMethod jwt.SigningMethod // The method to sign the JWT token for test purpose\n}{\n\t{\n\t\t\"invalid JWT\",\n\t\t\"thisisnotreallyajwt\",\n\t\tdefaultKeyFunc,\n\t\tnil,\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenMalformed},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"invalid JSON claim\",\n\t\t\"eyJhbGciOiJSUzI1NiIsInppcCI6IkRFRiJ9.eNqqVkqtKFCyMjQ1s7Q0sbA0MtFRyk3NTUot8kxRslIKLbZQggn4JeamAoUcfRz99HxcXRWeze172tr4bFq7Ui0AAAD__w.jBXD4LT4aq4oXTgDoPkiV6n4QdSZPZI1Z4J8MWQC42aHK0oXwcovEU06dVbtB81TF-2byuu0-qi8J0GUttODT67k6gCl6DV_iuCOV7gczwTcvKslotUvXzoJ2wa0QuujnjxLEE50r0p6k0tsv_9OIFSUZzDksJFYNPlJH2eFG55DROx4TsOz98az37SujZi9GGbTc9SLgzFHPrHMrovRZ5qLC_w4JrdtsLzBBI11OQJgRYwV8fQf4O8IsMkHtetjkN7dKgUkJtRarNWOk76rpTPppLypiLU4_J0-wrElLMh1TzUVZW6Fz2cDHDDBACJgMmKQ2pOFEDK_vYZN74dLCF5GiTZV6DbXhNxO7lqT7JUN4a3p2z96G7WNRjblf2qZeuYdQvkIsiK-rCbSIE836XeY5gaBgkOzuEvzl_tMrpRmb5Oox1ibOfVT2KBh9Lvqsb1XbQjCio2CLE2ViCLqoe0AaRqlUyrk3n8BIG-r0IW4dcw96CEryEMIjsjVp9mtPXamJzf391kt8Rf3iRBqwv3zP7Plg1ResXbmsFUgOflAUPcYmfLug4W3W52ntcUlTHAKXrNfaJL9QQiYAaDukG-ZHDytsOWTuuXw7lVxjt-XYi1VbRAIjh1aIYSELEmEpE4Ny74htQtywYXMQNfJpB0nNn8IiWakgcYYMJ0TmKM\",\n\t\tdefaultKeyFunc,\n\t\tnil,\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenMalformed},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"bearer in JWT\",\n\t\t\"bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tdefaultKeyFunc,\n\t\tnil,\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenMalformed},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t/*\n\t\tcustom claims with JSON Data Types\n\t\tPayload:\n\t\t{\n\t\t  \"stringClaim\": \"string\",\n\t\t  \"intClaim\": -1,\n\t\t  \"boolClaim\": true,\n\t\t  \"uintClaim\": 1,\n\t\t  \"floatClaim\": 5.01,\n\t\t  \"sliceClaim\": [\n\t\t    -1,\n\t\t    0,\n\t\t    1\n\t\t  ],\n\t\t  \"objectClaim\": {\n\t\t    \"property\": \"something\"\n\t\t  },\n\t\t  \"nilPointerClaim\": null\n\t\t}\n\t*/\n\t{\n\t\t\"custom claims with JSON Data Types\",\n\t\t\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdHJpbmdDbGFpbSI6InN0cmluZyIsImludENsYWltIjotMSwiYm9vbENsYWltIjp0cnVlLCJ1aW50Q2xhaW0iOjEsImZsb2F0Q2xhaW0iOjUuMDEsInNsaWNlQ2xhaW0iOlstMSwwLDFdLCJvYmplY3RDbGFpbSI6eyJwcm9wZXJ0eSI6InNvbWV0aGluZyJ9LCJuaWxQb2ludGVyQ2xhaW0iOm51bGx9.08Q-I2ISws_DaZnxjZ39j5EtmhGSo6dwigxMlV42kKeSVIiMDAGdukYDMJvyurrvLV19gMWkAHdLp5j23qHhP_KUcj2dKlooIFkkRvp2XuTdOtGsyCy5X7jHMYhyR4pMW7y5jor1njrxuDPBvI_oxjs-EZEYqjk8Su4_LAS5AmI2wnTR5DQAAF70WqZAvfnEwavtKSuSCYZS8ZcblBvhfufnjoXtOHbjFnIN1hEzbiLFOe-Ka_tGegYy-7RgX65ohlAFV3By48rXAcr6PLk5eG2Hz9ZSJ6GR6bozqwwnbbm7loiJYtIw7nwQSeo2sRHCw9RFG61Rq33XNlUD_kYC8Q\",\n\t\tdefaultKeyFunc,\n\t\t&customClaimsWithDifferentTypes{\n\t\t\tStringClaim: \"string\",\n\t\t\tIntClaim:    -1,\n\t\t\tBoolClaim:   true,\n\t\t\tUintClaim:   1,\n\t\t\tFloatClaim:  5.01,\n\t\t\tSliceClaim:  []int{-1, 0, 1},\n\t\t\tObjectClaim: CustomObject{\n\t\t\t\tProperty: \"something\",\n\t\t\t},\n\t\t\tNilPointerClaim: nil,\n\t\t},\n\t\ttrue,\n\t\tnil,\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"multiple keys, last matches\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleLastKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"multiple keys not []interface{} type, all match\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleAltTypedKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"multiple keys, first matches\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleFirstKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"public keys slice, not allowed\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleVerificationKeysFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic expired\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"exp\": float64(time.Now().Unix() - 100)},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenExpired},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic nbf\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"nbf\": float64(time.Now().Unix() + 100)},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenNotValidYet},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"expired and nbf\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"nbf\": float64(time.Now().Unix() + 100), \"exp\": float64(time.Now().Unix() - 100)},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenNotValidYet, jwt.ErrTokenExpired},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic invalid\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid, rsa.ErrVerification},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic nokeyfunc\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tnilKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenUnverifiable},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic nokey\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\temptyKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"multiple nokey\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleEmptyKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"empty verification key set\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\temptyVerificationKeySetFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenUnverifiable},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"zero length key list\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\tmultipleZeroKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"basic errorkey\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\terrorKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenUnverifiable, errKeyFuncError},\n\t\tnil,\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"invalid signing method\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tjwt.NewParser(jwt.WithValidMethods([]string{\"HS256\"})),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"valid RSA signing method\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithValidMethods([]string{\"RS256\", \"HS256\"})),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"ECDSA signing method not accepted\",\n\t\t\"\",\n\t\tecdsaKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenSignatureInvalid},\n\t\tjwt.NewParser(jwt.WithValidMethods([]string{\"RS256\", \"HS256\"})),\n\t\tjwt.SigningMethodES256,\n\t},\n\t{\n\t\t\"valid ECDSA signing method\",\n\t\t\"\",\n\t\tecdsaKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithValidMethods([]string{\"HS256\", \"ES256\"})),\n\t\tjwt.SigningMethodES256,\n\t},\n\t{\n\t\t\"JSON Number\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": json.Number(\"123.4\")},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"JSON Number - basic expired\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"exp\": json.Number(fmt.Sprintf(\"%v\", time.Now().Unix()-100))},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenExpired},\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"JSON Number - basic nbf\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"nbf\": json.Number(fmt.Sprintf(\"%v\", time.Now().Unix()+100))},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenNotValidYet},\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"JSON Number - expired and nbf\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"nbf\": json.Number(fmt.Sprintf(\"%v\", time.Now().Unix()+100)), \"exp\": json.Number(fmt.Sprintf(\"%v\", time.Now().Unix()-100))},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenNotValidYet, jwt.ErrTokenExpired},\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"SkipClaimsValidation during token parsing\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\tjwt.MapClaims{\"foo\": \"bar\", \"nbf\": json.Number(fmt.Sprintf(\"%v\", time.Now().Unix()+100))},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithJSONNumber(), jwt.WithoutClaimsValidation()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * 10)),\n\t\t},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - single aud\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{\n\t\t\tAudience: jwt.ClaimStrings{\"test\"},\n\t\t},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - multiple aud\",\n\t\t\"\",\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{\n\t\t\tAudience: jwt.ClaimStrings{\"test\", \"test\"},\n\t\t},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - single aud with wrong type\",\n\t\t\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOjF9.8mAIDUfZNQT3TGm1QFIQp91OCpJpQpbB1-m9pA2mkHc\", // { \"aud\": 1 }\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{\n\t\t\tAudience: nil, // because of the unmarshal error, this will be empty\n\t\t},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenMalformed},\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - multiple aud with wrong types\",\n\t\t\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdCIsMV19.htEBUf7BVbfSmVoTFjXf3y6DLmDUuLy1vTJ14_EX7Ws\", // { \"aud\": [\"test\", 1] }\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{\n\t\t\tAudience: nil, // because of the unmarshal error, this will be empty\n\t\t},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenMalformed},\n\t\tjwt.NewParser(jwt.WithJSONNumber()),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - nbf with 60s skew\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{NotBefore: jwt.NewNumericDate(time.Now().Add(time.Second * 100))},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenNotValidYet},\n\t\tjwt.NewParser(jwt.WithLeeway(time.Minute)),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"RFC7519 Claims - nbf with 120s skew\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{NotBefore: jwt.NewNumericDate(time.Now().Add(time.Second * 100))},\n\t\ttrue,\n\t\tnil,\n\t\tjwt.NewParser(jwt.WithLeeway(2 * time.Minute)),\n\t\tjwt.SigningMethodRS256,\n\t},\n\t{\n\t\t\"rejects if exp is required but missing\",\n\t\t\"\", // autogen\n\t\tdefaultKeyFunc,\n\t\t&jwt.RegisteredClaims{},\n\t\tfalse,\n\t\t[]error{jwt.ErrTokenInvalidClaims},\n\t\tjwt.NewParser(jwt.WithExpirationRequired()),\n\t\tjwt.SigningMethodRS256,\n\t},\n}\n\n// signToken creates and returns a signed JWT token using signingMethod.\nfunc signToken(claims jwt.Claims, signingMethod jwt.SigningMethod) string {\n\tvar privateKey any\n\tswitch signingMethod {\n\tcase jwt.SigningMethodRS256:\n\t\tprivateKey = jwtTestRSAPrivateKey\n\tcase jwt.SigningMethodES256:\n\t\tprivateKey = jwtTestEC256PrivateKey\n\tdefault:\n\t\treturn \"\"\n\t}\n\treturn test.MakeSampleToken(claims, signingMethod, privateKey)\n}\n\nfunc TestParser_Parse(t *testing.T) {\n\t// Iterate over test data set and run tests\n\tfor _, data := range jwtTestData {\n\t\tt.Run(data.name, func(t *testing.T) {\n\t\t\t// If the token string is blank, use helper function to generate string\n\t\t\tif data.tokenString == \"\" {\n\t\t\t\tdata.tokenString = signToken(data.claims, data.signingMethod)\n\t\t\t}\n\n\t\t\t// Parse the token\n\t\t\tvar token *jwt.Token\n\t\t\tvar err error\n\t\t\tvar parser = data.parser\n\t\t\tif parser == nil {\n\t\t\t\tparser = jwt.NewParser()\n\t\t\t}\n\t\t\t// Figure out correct claims type\n\t\t\tswitch data.claims.(type) {\n\t\t\tcase jwt.MapClaims:\n\t\t\t\ttoken, err = parser.ParseWithClaims(data.tokenString, jwt.MapClaims{}, data.keyfunc)\n\t\t\tcase *jwt.RegisteredClaims:\n\t\t\t\ttoken, err = parser.ParseWithClaims(data.tokenString, &jwt.RegisteredClaims{}, data.keyfunc)\n\t\t\tcase *customClaimsWithDifferentTypes:\n\t\t\t\ttoken, err = parser.ParseWithClaims(data.tokenString, &customClaimsWithDifferentTypes{}, data.keyfunc)\n\t\t\tcase nil:\n\t\t\t\ttoken, err = parser.ParseWithClaims(data.tokenString, nil, data.keyfunc)\n\t\t\t}\n\n\t\t\t// Verify result matches expectation\n\t\t\tif data.claims != nil && !reflect.DeepEqual(data.claims, token.Claims) {\n\t\t\t\tt.Errorf(\"[%v] Claims mismatch. Expecting: %v  Got: %v\", data.name, data.claims, token.Claims)\n\t\t\t}\n\n\t\t\tif data.valid && err != nil {\n\t\t\t\tt.Errorf(\"[%v] Error while verifying token: %T:%v\", data.name, err, err)\n\t\t\t}\n\n\t\t\tif !data.valid && err == nil {\n\t\t\t\tt.Errorf(\"[%v] Invalid token passed validation\", data.name)\n\t\t\t}\n\n\t\t\t// Since the returned token is nil in the ErrTokenMalformed, we\n\t\t\t// cannot make the comparison here\n\t\t\tif !errors.Is(err, jwt.ErrTokenMalformed) &&\n\t\t\t\t((err == nil && !token.Valid) || (err != nil && token.Valid)) {\n\t\t\t\tt.Errorf(\"[%v] Inconsistent behavior between returned error and token.Valid\", data.name)\n\t\t\t}\n\n\t\t\tif data.err != nil {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Errorf(\"[%v] Expecting error(s). Didn't get one.\", data.name)\n\t\t\t\t} else {\n\t\t\t\t\tvar all = false\n\t\t\t\t\tfor _, e := range data.err {\n\t\t\t\t\t\tall = errors.Is(err, e)\n\t\t\t\t\t}\n\n\t\t\t\t\tif !all {\n\t\t\t\t\t\tt.Errorf(\"[%v] Errors don't match expectation.  %v should contain all of %v\", data.name, err, data.err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif data.valid {\n\t\t\t\tif len(token.Signature) == 0 {\n\t\t\t\t\tt.Errorf(\"[%v] Signature is left unpopulated after parsing\", data.name)\n\t\t\t\t}\n\t\t\t\tif !token.Valid {\n\t\t\t\t\t// The 'Valid' field should be set to true when invoking Parse()\n\t\t\t\t\tt.Errorf(\"[%v] Token.Valid field mismatch. Expecting true, got %v\", data.name, token.Valid)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParser_ParseUnverified(t *testing.T) {\n\t// Iterate over test data set and run tests\n\tfor _, data := range jwtTestData {\n\t\t// Skip test data, that intentionally contains malformed tokens, as they would lead to an error\n\t\tif len(data.err) == 1 && errors.Is(data.err[0], jwt.ErrTokenMalformed) {\n\t\t\tcontinue\n\t\t}\n\n\t\tt.Run(data.name, func(t *testing.T) {\n\t\t\t// If the token string is blank, use helper function to generate string\n\t\t\tif data.tokenString == \"\" {\n\t\t\t\tdata.tokenString = signToken(data.claims, data.signingMethod)\n\t\t\t}\n\n\t\t\t// Parse the token\n\t\t\tvar token *jwt.Token\n\t\t\tvar err error\n\t\t\tvar parser = data.parser\n\t\t\tif parser == nil {\n\t\t\t\tparser = new(jwt.Parser)\n\t\t\t}\n\t\t\t// Figure out correct claims type\n\t\t\tswitch data.claims.(type) {\n\t\t\tcase jwt.MapClaims:\n\t\t\t\ttoken, _, err = parser.ParseUnverified(data.tokenString, jwt.MapClaims{})\n\t\t\tcase *jwt.RegisteredClaims:\n\t\t\t\ttoken, _, err = parser.ParseUnverified(data.tokenString, &jwt.RegisteredClaims{})\n\t\t\tcase *customClaimsWithDifferentTypes:\n\t\t\t\ttoken, _, err = parser.ParseUnverified(data.tokenString, &customClaimsWithDifferentTypes{})\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"[%v] Invalid token\", data.name)\n\t\t\t}\n\n\t\t\t// Verify result matches expectation\n\t\t\tif !reflect.DeepEqual(data.claims, token.Claims) {\n\t\t\t\tt.Errorf(\"[%v] Claims mismatch. Expecting: %v  Got: %v\", data.name, data.claims, token.Claims)\n\t\t\t}\n\n\t\t\tif data.valid && err != nil {\n\t\t\t\tt.Errorf(\"[%v] Error while verifying token: %T:%v\", data.name, err, err)\n\t\t\t}\n\t\t\tif token.Valid {\n\t\t\t\t// The 'Valid' field should not be set to true when invoking ParseUnverified()\n\t\t\t\tt.Errorf(\"[%v] Token.Valid field mismatch. Expecting false, got %v\", data.name, token.Valid)\n\t\t\t}\n\t\t\tif len(token.Signature) == 0 {\n\t\t\t\t// The 'Signature' should always be populated.\n\t\t\t\tt.Errorf(\"[%v] Token.Signature field mismatch. Expecting non-nil, got %v\", data.name, token.Signature)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar setPaddingTestData = []struct {\n\tname          string\n\ttokenString   string\n\tclaims        jwt.Claims\n\tpaddedDecode  bool\n\tstrictDecode  bool\n\tsigningMethod jwt.SigningMethod\n\tkeyfunc       jwt.Keyfunc\n\tvalid         bool\n}{\n\t{\n\t\tname:          \"Validated non-padded token with padding disabled\",\n\t\ttokenString:   \"\",\n\t\tclaims:        jwt.MapClaims{\"foo\": \"paddedbar\"},\n\t\tpaddedDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname:          \"Validated non-padded token with padding enabled\",\n\t\ttokenString:   \"\",\n\t\tclaims:        jwt.MapClaims{\"foo\": \"paddedbar\"},\n\t\tpaddedDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname:          \"Error for padded token with padding disabled\",\n\t\ttokenString:   \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ==.20kGGJaYekGTRFf8b0TwhuETcR8lv5z2363X5jf7G1yTWVTwOmte5Ii8L8_OQbYwPoiVHmZY6iJPbt_DhCN42AeFY74BcsUhR-BVrYUVhKK0RppuzEcSlILDNeQsJDLEL035CPm1VO6Jrgk7enQPIctVxUesRgswP71OpGvJxy3j1k_J8p0WzZvRZTe1D_2Misa0UDGwnEIHhmr97fIpMSZjFxlcygQw8QN34IHLHIXMaTY1eiCf4CCr6rOS9wUeu7P3CPkmFq9XhxBT_LLCmIMhHnxP5x27FUJE_JZlfek0MmARcrhpsZS2sFhHAiWrjxjOE27jkDtv1nEwn65wMw==\",\n\t\tclaims:        jwt.MapClaims{\"foo\": \"paddedbar\"},\n\t\tpaddedDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         false,\n\t},\n\t{\n\t\tname:          \"Validated padded token with padding enabled\",\n\t\ttokenString:   \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ==.20kGGJaYekGTRFf8b0TwhuETcR8lv5z2363X5jf7G1yTWVTwOmte5Ii8L8_OQbYwPoiVHmZY6iJPbt_DhCN42AeFY74BcsUhR-BVrYUVhKK0RppuzEcSlILDNeQsJDLEL035CPm1VO6Jrgk7enQPIctVxUesRgswP71OpGvJxy3j1k_J8p0WzZvRZTe1D_2Misa0UDGwnEIHhmr97fIpMSZjFxlcygQw8QN34IHLHIXMaTY1eiCf4CCr6rOS9wUeu7P3CPkmFq9XhxBT_LLCmIMhHnxP5x27FUJE_JZlfek0MmARcrhpsZS2sFhHAiWrjxjOE27jkDtv1nEwn65wMw==\",\n\t\tclaims:        jwt.MapClaims{\"foo\": \"paddedbar\"},\n\t\tpaddedDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname:          \"Error for example padded token with padding disabled\",\n\t\ttokenString:   \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3grw==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         false,\n\t},\n\t{\n\t\tname:          \"Validated example padded token with padding enabled\",\n\t\ttokenString:   \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3grw==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         true,\n\t},\n\t// DecodeStrict tests, DecodePaddingAllowed=false\n\t{\n\t\tname: \"Validated non-padded token with padding disabled, non-strict decode, non-tweaked signature\",\n\t\ttokenString: \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ.bI15h-7mN0f-2diX5I4ErgNQy1uM-rJS5Sz7O0iTWtWSBxY1h6wy8Ywxe5EZTEO6GiIfk7Lk-72Ex-c5aA40QKhPwWB9BJ8O_LfKpezUVBOn0jRItDnVdsk4ccl2zsOVkbA4U4QvdrSbOYMbwoRHzDXfTFpoeMWtn3ez0aENJ8dh4E1echHp5ByI9Pu2aBsvM1WVcMt_BySweCL3f4T7jNZeXDr7Txd00yUd2gdsHYPjXorOvsgaBKN5GLsWd1zIY5z-2gCC8CRSN-IJ4NNX5ifh7l-bOXE2q7szTqa9pvyE9y6TQJhNMSE2FotRce_TOPBWgGpQ-K2I7E8x7wZ8O\" +\n\t\t\t\"g\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  false,\n\t\tstrictDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Validated non-padded token with padding disabled, non-strict decode, tweaked signature\",\n\t\ttokenString: \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ.bI15h-7mN0f-2diX5I4ErgNQy1uM-rJS5Sz7O0iTWtWSBxY1h6wy8Ywxe5EZTEO6GiIfk7Lk-72Ex-c5aA40QKhPwWB9BJ8O_LfKpezUVBOn0jRItDnVdsk4ccl2zsOVkbA4U4QvdrSbOYMbwoRHzDXfTFpoeMWtn3ez0aENJ8dh4E1echHp5ByI9Pu2aBsvM1WVcMt_BySweCL3f4T7jNZeXDr7Txd00yUd2gdsHYPjXorOvsgaBKN5GLsWd1zIY5z-2gCC8CRSN-IJ4NNX5ifh7l-bOXE2q7szTqa9pvyE9y6TQJhNMSE2FotRce_TOPBWgGpQ-K2I7E8x7wZ8O\" +\n\t\t\t\"h\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  false,\n\t\tstrictDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Validated non-padded token with padding disabled, strict decode, non-tweaked signature\",\n\t\ttokenString: \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ.bI15h-7mN0f-2diX5I4ErgNQy1uM-rJS5Sz7O0iTWtWSBxY1h6wy8Ywxe5EZTEO6GiIfk7Lk-72Ex-c5aA40QKhPwWB9BJ8O_LfKpezUVBOn0jRItDnVdsk4ccl2zsOVkbA4U4QvdrSbOYMbwoRHzDXfTFpoeMWtn3ez0aENJ8dh4E1echHp5ByI9Pu2aBsvM1WVcMt_BySweCL3f4T7jNZeXDr7Txd00yUd2gdsHYPjXorOvsgaBKN5GLsWd1zIY5z-2gCC8CRSN-IJ4NNX5ifh7l-bOXE2q7szTqa9pvyE9y6TQJhNMSE2FotRce_TOPBWgGpQ-K2I7E8x7wZ8O\" +\n\t\t\t\"g\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  false,\n\t\tstrictDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Error for non-padded token with padding disabled, strict decode, tweaked signature\",\n\t\ttokenString: \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ.bI15h-7mN0f-2diX5I4ErgNQy1uM-rJS5Sz7O0iTWtWSBxY1h6wy8Ywxe5EZTEO6GiIfk7Lk-72Ex-c5aA40QKhPwWB9BJ8O_LfKpezUVBOn0jRItDnVdsk4ccl2zsOVkbA4U4QvdrSbOYMbwoRHzDXfTFpoeMWtn3ez0aENJ8dh4E1echHp5ByI9Pu2aBsvM1WVcMt_BySweCL3f4T7jNZeXDr7Txd00yUd2gdsHYPjXorOvsgaBKN5GLsWd1zIY5z-2gCC8CRSN-IJ4NNX5ifh7l-bOXE2q7szTqa9pvyE9y6TQJhNMSE2FotRce_TOPBWgGpQ-K2I7E8x7wZ8O\" +\n\t\t\t\"h\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  false,\n\t\tstrictDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodRS256,\n\t\tkeyfunc:       defaultKeyFunc,\n\t\tvalid:         false,\n\t},\n\t// DecodeStrict tests, DecodePaddingAllowed=true\n\t{\n\t\tname: \"Validated padded token with padding enabled, non-strict decode, non-tweaked signature\",\n\t\ttokenString: \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3gr\" +\n\t\t\t\"w==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  true,\n\t\tstrictDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Validated padded token with padding enabled, non-strict decode, tweaked signature\",\n\t\ttokenString: \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3gr\" +\n\t\t\t\"x==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  true,\n\t\tstrictDecode:  false,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Validated padded token with padding enabled, strict decode, non-tweaked signature\",\n\t\ttokenString: \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3gr\" +\n\t\t\t\"w==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  true,\n\t\tstrictDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         true,\n\t},\n\t{\n\t\tname: \"Error for padded token with padding enabled, strict decode, tweaked signature\",\n\t\ttokenString: \"eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3gr\" +\n\t\t\t\"x==\",\n\t\tclaims:        nil,\n\t\tpaddedDecode:  true,\n\t\tstrictDecode:  true,\n\t\tsigningMethod: jwt.SigningMethodES256,\n\t\tkeyfunc:       paddedKeyFunc,\n\t\tvalid:         false,\n\t},\n}\n\n// Extension of Parsing, this is to test out functionality specific to switching codecs with padding.\nfunc TestSetPadding(t *testing.T) {\n\tfor _, data := range setPaddingTestData {\n\t\tt.Run(data.name, func(t *testing.T) {\n\t\t\t// If the token string is blank, use helper function to generate string\n\t\t\tif data.tokenString == \"\" {\n\t\t\t\tdata.tokenString = signToken(data.claims, data.signingMethod)\n\t\t\t}\n\n\t\t\t// Parse the token\n\t\t\tvar token *jwt.Token\n\t\t\tvar err error\n\t\t\tvar opts = []jwt.ParserOption{jwt.WithoutClaimsValidation()}\n\n\t\t\tif data.paddedDecode {\n\t\t\t\topts = append(opts, jwt.WithPaddingAllowed())\n\t\t\t}\n\t\t\tif data.strictDecode {\n\t\t\t\topts = append(opts, jwt.WithStrictDecoding())\n\t\t\t}\n\n\t\t\tparser := jwt.NewParser(opts...)\n\n\t\t\t// Figure out correct claims type\n\t\t\ttoken, err = parser.ParseWithClaims(data.tokenString, jwt.MapClaims{}, data.keyfunc)\n\n\t\t\tif (err == nil) != data.valid || token.Valid != data.valid {\n\t\t\t\tt.Errorf(\"[%v] Error Parsing Token with decoding padding set to %v: %v\",\n\t\t\t\t\tdata.name,\n\t\t\t\t\tdata.paddedDecode,\n\t\t\t\t\terr,\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkParseUnverified(b *testing.B) {\n\t// Iterate over test data set and run tests\n\tfor _, data := range jwtTestData {\n\t\t// If the token string is blank, use helper function to generate string\n\t\tif data.tokenString == \"\" {\n\t\t\tdata.tokenString = signToken(data.claims, data.signingMethod)\n\t\t}\n\n\t\t// Parse the token\n\t\tvar parser = data.parser\n\t\tif parser == nil {\n\t\t\tparser = new(jwt.Parser)\n\t\t}\n\t\t// Figure out correct claims type\n\t\tswitch data.claims.(type) {\n\t\tcase jwt.MapClaims:\n\t\t\tb.Run(\"map_claims\", func(b *testing.B) {\n\t\t\t\tbenchmarkParsing(b, parser, data.tokenString, jwt.MapClaims{})\n\t\t\t})\n\t\tcase *jwt.RegisteredClaims:\n\t\t\tb.Run(\"registered_claims\", func(b *testing.B) {\n\t\t\t\tbenchmarkParsing(b, parser, data.tokenString, &jwt.RegisteredClaims{})\n\t\t\t})\n\t\t}\n\t}\n}\n\n// Helper method for benchmarking various parsing methods\nfunc benchmarkParsing(b *testing.B, parser *jwt.Parser, tokenString string, claims jwt.Claims) {\n\tb.Helper()\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, err := parser.ParseUnverified(tokenString, jwt.MapClaims{})\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\n// Helper method for benchmarking various signing methods\nfunc benchmarkSigning(b *testing.B, method jwt.SigningMethod, key any) {\n\tb.Helper()\n\tt := jwt.New(method)\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif _, err := t.SignedString(key); err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "registered_claims.go",
    "content": "package jwt\n\n// RegisteredClaims are a structured version of the JWT Claims Set,\n// restricted to Registered Claim Names, as referenced at\n// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\n//\n// This type can be used on its own, but then additional private and\n// public claims embedded in the JWT will not be parsed. The typical use-case\n// therefore is to embedded this in a user-defined claim type.\n//\n// See examples for how to use this with your own claim types.\ntype RegisteredClaims struct {\n\t// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1\n\tIssuer string `json:\"iss,omitempty\"`\n\n\t// the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2\n\tSubject string `json:\"sub,omitempty\"`\n\n\t// the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3\n\tAudience ClaimStrings `json:\"aud,omitempty\"`\n\n\t// the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4\n\tExpiresAt *NumericDate `json:\"exp,omitempty\"`\n\n\t// the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5\n\tNotBefore *NumericDate `json:\"nbf,omitempty\"`\n\n\t// the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6\n\tIssuedAt *NumericDate `json:\"iat,omitempty\"`\n\n\t// the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7\n\tID string `json:\"jti,omitempty\"`\n}\n\n// GetExpirationTime implements the Claims interface.\nfunc (c RegisteredClaims) GetExpirationTime() (*NumericDate, error) {\n\treturn c.ExpiresAt, nil\n}\n\n// GetNotBefore implements the Claims interface.\nfunc (c RegisteredClaims) GetNotBefore() (*NumericDate, error) {\n\treturn c.NotBefore, nil\n}\n\n// GetIssuedAt implements the Claims interface.\nfunc (c RegisteredClaims) GetIssuedAt() (*NumericDate, error) {\n\treturn c.IssuedAt, nil\n}\n\n// GetAudience implements the Claims interface.\nfunc (c RegisteredClaims) GetAudience() (ClaimStrings, error) {\n\treturn c.Audience, nil\n}\n\n// GetIssuer implements the Claims interface.\nfunc (c RegisteredClaims) GetIssuer() (string, error) {\n\treturn c.Issuer, nil\n}\n\n// GetSubject implements the Claims interface.\nfunc (c RegisteredClaims) GetSubject() (string, error) {\n\treturn c.Subject, nil\n}\n"
  },
  {
    "path": "request/doc.go",
    "content": "// Utility package for extracting JWT tokens from\n// HTTP requests.\n//\n// The main function is ParseFromRequest and it's WithClaims variant.\n// See examples for how to use the various Extractor implementations\n// or roll your own.\npackage request\n"
  },
  {
    "path": "request/extractor.go",
    "content": "package request\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// Errors\nvar (\n\tErrNoTokenInRequest = errors.New(\"no token present in request\")\n)\n\n// Extractor is an interface for extracting a token from an HTTP request.\n// The ExtractToken method should return a token string or an error.\n// If no token is present, you must return ErrNoTokenInRequest.\ntype Extractor interface {\n\tExtractToken(*http.Request) (string, error)\n}\n\n// HeaderExtractor is an extractor for finding a token in a header.\n// Looks at each specified header in order until there's a match\ntype HeaderExtractor []string\n\nfunc (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) {\n\t// loop over header names and return the first one that contains data\n\tfor _, header := range e {\n\t\tif ah := req.Header.Get(header); ah != \"\" {\n\t\t\treturn ah, nil\n\t\t}\n\t}\n\treturn \"\", ErrNoTokenInRequest\n}\n\n// ArgumentExtractor extracts a token from request arguments.  This includes a POSTed form or\n// GET URL arguments.  Argument names are tried in order until there's a match.\n// This extractor calls `ParseMultipartForm` on the request\ntype ArgumentExtractor []string\n\nfunc (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) {\n\t// Make sure form is parsed. We are explicitly ignoring errors at this point\n\t_ = req.ParseMultipartForm(10e6)\n\n\t// loop over arg names and return the first one that contains data\n\tfor _, arg := range e {\n\t\tif ah := req.Form.Get(arg); ah != \"\" {\n\t\t\treturn ah, nil\n\t\t}\n\t}\n\n\treturn \"\", ErrNoTokenInRequest\n}\n\n// MultiExtractor tries Extractors in order until one returns a token string or an error occurs\ntype MultiExtractor []Extractor\n\nfunc (e MultiExtractor) ExtractToken(req *http.Request) (string, error) {\n\t// loop over header names and return the first one that contains data\n\tfor _, extractor := range e {\n\t\tif tok, err := extractor.ExtractToken(req); tok != \"\" {\n\t\t\treturn tok, nil\n\t\t} else if !errors.Is(err, ErrNoTokenInRequest) {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn \"\", ErrNoTokenInRequest\n}\n\n// PostExtractionFilter wraps an Extractor in this to post-process the value before it's handed off.\n// See AuthorizationHeaderExtractor for an example\ntype PostExtractionFilter struct {\n\tExtractor\n\tFilter func(string) (string, error)\n}\n\nfunc (e *PostExtractionFilter) ExtractToken(req *http.Request) (string, error) {\n\tif tok, err := e.Extractor.ExtractToken(req); tok != \"\" {\n\t\treturn e.Filter(tok)\n\t} else {\n\t\treturn \"\", err\n\t}\n}\n\n// BearerExtractor extracts a token from the Authorization header.\n// The header is expected to match the format \"Bearer XX\", where \"XX\" is the\n// JWT token.\ntype BearerExtractor struct{}\n\nfunc (e BearerExtractor) ExtractToken(req *http.Request) (string, error) {\n\ttokenHeader := req.Header.Get(\"Authorization\")\n\t// The usual convention is for \"Bearer\" to be title-cased. However, there's no\n\t// strict rule around this, and it's best to follow the robustness principle here.\n\tif len(tokenHeader) < 7 || !strings.EqualFold(tokenHeader[:7], \"bearer \") {\n\t\treturn \"\", ErrNoTokenInRequest\n\t}\n\treturn tokenHeader[7:], nil\n}\n"
  },
  {
    "path": "request/extractor_example_test.go",
    "content": "package request\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n)\n\nconst (\n\texampleTokenA = \"A\"\n)\n\nfunc ExampleHeaderExtractor() {\n\treq := makeExampleRequest(\"GET\", \"/\", map[string]string{\"Token\": exampleTokenA}, nil)\n\ttokenString, err := HeaderExtractor{\"Token\"}.ExtractToken(req)\n\tif err == nil {\n\t\tfmt.Println(tokenString)\n\t} else {\n\t\tfmt.Println(err)\n\t}\n\t// Output: A\n}\n\nfunc ExampleArgumentExtractor() {\n\treq := makeExampleRequest(\"GET\", \"/\", nil, url.Values{\"token\": {extractorTestTokenA}})\n\ttokenString, err := ArgumentExtractor{\"token\"}.ExtractToken(req)\n\tif err == nil {\n\t\tfmt.Println(tokenString)\n\t} else {\n\t\tfmt.Println(err)\n\t}\n\t// Output: A\n}\n"
  },
  {
    "path": "request/extractor_test.go",
    "content": "package request\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nvar extractorTestTokenA = \"A\"\nvar extractorTestTokenB = \"B\"\n\nvar extractorTestData = []struct {\n\tname      string\n\textractor Extractor\n\theaders   map[string]string\n\tquery     url.Values\n\ttoken     string\n\terr       error\n}{\n\t{\n\t\tname:      \"simple header\",\n\t\textractor: HeaderExtractor{\"Foo\"},\n\t\theaders:   map[string]string{\"Foo\": extractorTestTokenA},\n\t\tquery:     nil,\n\t\ttoken:     extractorTestTokenA,\n\t\terr:       nil,\n\t},\n\t{\n\t\tname:      \"simple argument\",\n\t\textractor: ArgumentExtractor{\"token\"},\n\t\theaders:   map[string]string{},\n\t\tquery:     url.Values{\"token\": {extractorTestTokenA}},\n\t\ttoken:     extractorTestTokenA,\n\t\terr:       nil,\n\t},\n\t{\n\t\tname: \"multiple extractors\",\n\t\textractor: MultiExtractor{\n\t\t\tHeaderExtractor{\"Foo\"},\n\t\t\tArgumentExtractor{\"token\"},\n\t\t},\n\t\theaders: map[string]string{\"Foo\": extractorTestTokenA},\n\t\tquery:   url.Values{\"token\": {extractorTestTokenB}},\n\t\ttoken:   extractorTestTokenA,\n\t\terr:     nil,\n\t},\n\t{\n\t\tname:      \"simple miss\",\n\t\textractor: HeaderExtractor{\"This-Header-Is-Not-Set\"},\n\t\theaders:   map[string]string{\"Foo\": extractorTestTokenA},\n\t\tquery:     nil,\n\t\ttoken:     \"\",\n\t\terr:       ErrNoTokenInRequest,\n\t},\n\t{\n\t\tname:      \"filter\",\n\t\textractor: AuthorizationHeaderExtractor,\n\t\theaders:   map[string]string{\"Authorization\": \"Bearer \" + extractorTestTokenA},\n\t\tquery:     nil,\n\t\ttoken:     extractorTestTokenA,\n\t\terr:       nil,\n\t},\n}\n\nfunc TestExtractor(t *testing.T) {\n\t// Bearer token request\n\tfor _, data := range extractorTestData {\n\t\t// Make request from test struct\n\t\tr := makeExampleRequest(\"GET\", \"/\", data.headers, data.query)\n\n\t\t// Test extractor\n\t\ttoken, err := data.extractor.ExtractToken(r)\n\t\tif token != data.token {\n\t\t\tt.Errorf(\"[%v] Expected token '%v'.  Got '%v'\", data.name, data.token, token)\n\t\t\tcontinue\n\t\t}\n\t\tif err != data.err {\n\t\t\tt.Errorf(\"[%v] Expected error '%v'.  Got '%v'\", data.name, data.err, err)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\nfunc makeExampleRequest(method, path string, headers map[string]string, urlArgs url.Values) *http.Request {\n\tr, _ := http.NewRequest(method, fmt.Sprintf(\"%v?%v\", path, urlArgs.Encode()), nil)\n\tfor k, v := range headers {\n\t\tr.Header.Set(k, v)\n\t}\n\treturn r\n}\n\nfunc TestBearerExtractor(t *testing.T) {\n\trequest := makeExampleRequest(\"POST\", \"https://example.com/\", map[string]string{\"Authorization\": \"Bearer ToKen\"}, nil)\n\ttoken, err := BearerExtractor{}.ExtractToken(request)\n\tif err != nil || token != \"ToKen\" {\n\t\tt.Errorf(\"ExtractToken did not return token, returned: %v, %v\", token, err)\n\t}\n\n\trequest = makeExampleRequest(\"POST\", \"https://example.com/\", map[string]string{\"Authorization\": \"Bearo ToKen\"}, nil)\n\ttoken, err = BearerExtractor{}.ExtractToken(request)\n\tif err == nil || token != \"\" {\n\t\tt.Errorf(\"ExtractToken did not return error, returned: %v, %v\", token, err)\n\t}\n\n\trequest = makeExampleRequest(\"POST\", \"https://example.com/\", map[string]string{\"Authorization\": \"BeArEr HeLO\"}, nil)\n\ttoken, err = BearerExtractor{}.ExtractToken(request)\n\tif err != nil || token != \"HeLO\" {\n\t\tt.Errorf(\"ExtractToken did not return token, returned: %v, %v\", token, err)\n\t}\n}\n"
  },
  {
    "path": "request/oauth2.go",
    "content": "package request\n\nimport (\n\t\"strings\"\n)\n\n// Strips 'Bearer ' prefix from bearer token string\nfunc stripBearerPrefixFromTokenString(tok string) (string, error) {\n\t// Should be a bearer token\n\tif len(tok) > 6 && strings.EqualFold(tok[:7], \"bearer \") {\n\t\treturn tok[7:], nil\n\t}\n\treturn tok, nil\n}\n\n// AuthorizationHeaderExtractor extracts a bearer token from Authorization header\n// Uses PostExtractionFilter to strip \"Bearer \" prefix from header\nvar AuthorizationHeaderExtractor = &PostExtractionFilter{\n\tHeaderExtractor{\"Authorization\"},\n\tstripBearerPrefixFromTokenString,\n}\n\n// OAuth2Extractor is an Extractor for OAuth2 access tokens.  Looks in 'Authorization'\n// header then 'access_token' argument for a token.\nvar OAuth2Extractor = &MultiExtractor{\n\tAuthorizationHeaderExtractor,\n\tArgumentExtractor{\"access_token\"},\n}\n"
  },
  {
    "path": "request/request.go",
    "content": "package request\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// ParseFromRequest extracts and parses a JWT token from an HTTP request.\n// This behaves the same as Parse, but accepts a request and an extractor\n// instead of a token string.  The Extractor interface allows you to define\n// the logic for extracting a token.  Several useful implementations are provided.\n//\n// You can provide options to modify parsing behavior\nfunc ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc, options ...ParseFromRequestOption) (token *jwt.Token, err error) {\n\t// Create basic parser struct\n\tp := &fromRequestParser{req, extractor, nil, nil}\n\n\t// Handle options\n\tfor _, option := range options {\n\t\toption(p)\n\t}\n\n\t// Set defaults\n\tif p.claims == nil {\n\t\tp.claims = jwt.MapClaims{}\n\t}\n\tif p.parser == nil {\n\t\tp.parser = &jwt.Parser{}\n\t}\n\n\t// perform extract\n\ttokenString, err := p.extractor.ExtractToken(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// perform parse\n\treturn p.parser.ParseWithClaims(tokenString, p.claims, keyFunc)\n}\n\n// ParseFromRequestWithClaims is an alias for ParseFromRequest but with custom Claims type.\n//\n// Deprecated: use ParseFromRequest and the WithClaims option\nfunc ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) {\n\treturn ParseFromRequest(req, extractor, keyFunc, WithClaims(claims))\n}\n\ntype fromRequestParser struct {\n\treq       *http.Request\n\textractor Extractor\n\tclaims    jwt.Claims\n\tparser    *jwt.Parser\n}\n\ntype ParseFromRequestOption func(*fromRequestParser)\n\n// WithClaims parses with custom claims\nfunc WithClaims(claims jwt.Claims) ParseFromRequestOption {\n\treturn func(p *fromRequestParser) {\n\t\tp.claims = claims\n\t}\n}\n\n// WithParser parses using a custom parser\nfunc WithParser(parser *jwt.Parser) ParseFromRequestOption {\n\treturn func(p *fromRequestParser) {\n\t\tp.parser = parser\n\t}\n}\n"
  },
  {
    "path": "request/request_test.go",
    "content": "package request\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/golang-jwt/jwt/v5/test\"\n)\n\nvar requestTestData = []struct {\n\tname      string\n\tclaims    jwt.MapClaims\n\textractor Extractor\n\theaders   map[string]string\n\tquery     url.Values\n\tvalid     bool\n}{\n\t{\n\t\t\"authorization bearer token\",\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tAuthorizationHeaderExtractor,\n\t\tmap[string]string{\"Authorization\": \"Bearer %v\"},\n\t\turl.Values{},\n\t\ttrue,\n\t},\n\t{\n\t\t\"oauth bearer token - header\",\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tOAuth2Extractor,\n\t\tmap[string]string{\"Authorization\": \"Bearer %v\"},\n\t\turl.Values{},\n\t\ttrue,\n\t},\n\t{\n\t\t\"oauth bearer token - url\",\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tOAuth2Extractor,\n\t\tmap[string]string{},\n\t\turl.Values{\"access_token\": {\"%v\"}},\n\t\ttrue,\n\t},\n\t{\n\t\t\"url token\",\n\t\tjwt.MapClaims{\"foo\": \"bar\"},\n\t\tArgumentExtractor{\"token\"},\n\t\tmap[string]string{},\n\t\turl.Values{\"token\": {\"%v\"}},\n\t\ttrue,\n\t},\n}\n\nfunc TestParseRequest(t *testing.T) {\n\t// load keys from disk\n\tprivateKey := test.LoadRSAPrivateKeyFromDisk(\"../test/sample_key\")\n\tpublicKey := test.LoadRSAPublicKeyFromDisk(\"../test/sample_key.pub\")\n\tkeyfunc := func(*jwt.Token) (any, error) {\n\t\treturn publicKey, nil\n\t}\n\n\t// Bearer token request\n\tfor _, data := range requestTestData {\n\t\t// Make token from claims\n\t\ttokenString := test.MakeSampleToken(data.claims, jwt.SigningMethodRS256, privateKey)\n\n\t\t// Make query string\n\t\tfor k, vv := range data.query {\n\t\t\tfor i, v := range vv {\n\t\t\t\tif strings.Contains(v, \"%v\") {\n\t\t\t\t\tdata.query[k][i] = fmt.Sprintf(v, tokenString)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Make request from test struct\n\t\tr, _ := http.NewRequest(\"GET\", fmt.Sprintf(\"/?%v\", data.query.Encode()), nil)\n\t\tfor k, v := range data.headers {\n\t\t\tif strings.Contains(v, \"%v\") {\n\t\t\t\tr.Header.Set(k, fmt.Sprintf(v, tokenString))\n\t\t\t} else {\n\t\t\t\tr.Header.Set(k, tokenString)\n\t\t\t}\n\t\t}\n\t\ttoken, err := ParseFromRequestWithClaims(r, data.extractor, jwt.MapClaims{}, keyfunc)\n\n\t\tif token == nil {\n\t\t\tt.Errorf(\"[%v] Token was not found: %v\", data.name, err)\n\t\t\tcontinue\n\t\t}\n\t\tif !reflect.DeepEqual(data.claims, token.Claims) {\n\t\t\tt.Errorf(\"[%v] Claims mismatch. Expecting: %v  Got: %v\", data.name, data.claims, token.Claims)\n\t\t}\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying token: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid token passed validation\", data.name)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "rsa.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n)\n\n// SigningMethodRSA implements the RSA family of signing methods.\n// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation\ntype SigningMethodRSA struct {\n\tName string\n\tHash crypto.Hash\n}\n\n// Specific instances for RS256 and company\nvar (\n\tSigningMethodRS256 *SigningMethodRSA\n\tSigningMethodRS384 *SigningMethodRSA\n\tSigningMethodRS512 *SigningMethodRSA\n)\n\nfunc init() {\n\t// RS256\n\tSigningMethodRS256 = &SigningMethodRSA{\"RS256\", crypto.SHA256}\n\tRegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod {\n\t\treturn SigningMethodRS256\n\t})\n\n\t// RS384\n\tSigningMethodRS384 = &SigningMethodRSA{\"RS384\", crypto.SHA384}\n\tRegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod {\n\t\treturn SigningMethodRS384\n\t})\n\n\t// RS512\n\tSigningMethodRS512 = &SigningMethodRSA{\"RS512\", crypto.SHA512}\n\tRegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod {\n\t\treturn SigningMethodRS512\n\t})\n}\n\nfunc (m *SigningMethodRSA) Alg() string {\n\treturn m.Name\n}\n\n// Verify implements token verification for the SigningMethod\n// For this signing method, must be an *rsa.PublicKey structure.\nfunc (m *SigningMethodRSA) Verify(signingString string, sig []byte, key any) error {\n\tvar rsaKey *rsa.PublicKey\n\tvar ok bool\n\n\tif rsaKey, ok = key.(*rsa.PublicKey); !ok {\n\t\treturn newError(\"RSA verify expects *rsa.PublicKey\", ErrInvalidKeyType)\n\t}\n\n\t// Create hasher\n\tif !m.Hash.Available() {\n\t\treturn ErrHashUnavailable\n\t}\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\t// Verify the signature\n\treturn rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)\n}\n\n// Sign implements token signing for the SigningMethod\n// For this signing method, must be an *rsa.PrivateKey structure.\nfunc (m *SigningMethodRSA) Sign(signingString string, key any) ([]byte, error) {\n\tvar rsaKey *rsa.PrivateKey\n\tvar ok bool\n\n\t// Validate type of key\n\tif rsaKey, ok = key.(*rsa.PrivateKey); !ok {\n\t\treturn nil, newError(\"RSA sign expects *rsa.PrivateKey\", ErrInvalidKeyType)\n\t}\n\n\t// Create the hasher\n\tif !m.Hash.Available() {\n\t\treturn nil, ErrHashUnavailable\n\t}\n\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\t// Sign the string and return the encoded bytes\n\tif sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil {\n\t\treturn sigBytes, nil\n\t} else {\n\t\treturn nil, err\n\t}\n}\n"
  },
  {
    "path": "rsa_pss.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n)\n\n// SigningMethodRSAPSS implements the RSAPSS family of signing methods signing methods\ntype SigningMethodRSAPSS struct {\n\t*SigningMethodRSA\n\tOptions *rsa.PSSOptions\n\t// VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS.\n\t// Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow\n\t// https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously.\n\t// See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details.\n\tVerifyOptions *rsa.PSSOptions\n}\n\n// Specific instances for RS/PS and company.\nvar (\n\tSigningMethodPS256 *SigningMethodRSAPSS\n\tSigningMethodPS384 *SigningMethodRSAPSS\n\tSigningMethodPS512 *SigningMethodRSAPSS\n)\n\nfunc init() {\n\t// PS256\n\tSigningMethodPS256 = &SigningMethodRSAPSS{\n\t\tSigningMethodRSA: &SigningMethodRSA{\n\t\t\tName: \"PS256\",\n\t\t\tHash: crypto.SHA256,\n\t\t},\n\t\tOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthEqualsHash,\n\t\t},\n\t\tVerifyOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthAuto,\n\t\t},\n\t}\n\tRegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod {\n\t\treturn SigningMethodPS256\n\t})\n\n\t// PS384\n\tSigningMethodPS384 = &SigningMethodRSAPSS{\n\t\tSigningMethodRSA: &SigningMethodRSA{\n\t\t\tName: \"PS384\",\n\t\t\tHash: crypto.SHA384,\n\t\t},\n\t\tOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthEqualsHash,\n\t\t},\n\t\tVerifyOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthAuto,\n\t\t},\n\t}\n\tRegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod {\n\t\treturn SigningMethodPS384\n\t})\n\n\t// PS512\n\tSigningMethodPS512 = &SigningMethodRSAPSS{\n\t\tSigningMethodRSA: &SigningMethodRSA{\n\t\t\tName: \"PS512\",\n\t\t\tHash: crypto.SHA512,\n\t\t},\n\t\tOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthEqualsHash,\n\t\t},\n\t\tVerifyOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthAuto,\n\t\t},\n\t}\n\tRegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod {\n\t\treturn SigningMethodPS512\n\t})\n}\n\n// Verify implements token verification for the SigningMethod.\n// For this verify method, key must be an rsa.PublicKey struct\nfunc (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key any) error {\n\tvar rsaKey *rsa.PublicKey\n\tswitch k := key.(type) {\n\tcase *rsa.PublicKey:\n\t\trsaKey = k\n\tdefault:\n\t\treturn newError(\"RSA-PSS verify expects *rsa.PublicKey\", ErrInvalidKeyType)\n\t}\n\n\t// Create hasher\n\tif !m.Hash.Available() {\n\t\treturn ErrHashUnavailable\n\t}\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\topts := m.Options\n\tif m.VerifyOptions != nil {\n\t\topts = m.VerifyOptions\n\t}\n\n\treturn rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts)\n}\n\n// Sign implements token signing for the SigningMethod.\n// For this signing method, key must be an rsa.PrivateKey struct\nfunc (m *SigningMethodRSAPSS) Sign(signingString string, key any) ([]byte, error) {\n\tvar rsaKey *rsa.PrivateKey\n\n\tswitch k := key.(type) {\n\tcase *rsa.PrivateKey:\n\t\trsaKey = k\n\tdefault:\n\t\treturn nil, newError(\"RSA-PSS sign expects *rsa.PrivateKey\", ErrInvalidKeyType)\n\t}\n\n\t// Create the hasher\n\tif !m.Hash.Available() {\n\t\treturn nil, ErrHashUnavailable\n\t}\n\n\thasher := m.Hash.New()\n\thasher.Write([]byte(signingString))\n\n\t// Sign the string and return the encoded bytes\n\tif sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil {\n\t\treturn sigBytes, nil\n\t} else {\n\t\treturn nil, err\n\t}\n}\n"
  },
  {
    "path": "rsa_pss_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"crypto/rsa\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/golang-jwt/jwt/v5/test\"\n)\n\nvar rsaPSSTestData = []struct {\n\tname        string\n\ttokenString string\n\talg         string\n\tclaims      map[string]any\n\tvalid       bool\n}{\n\t{\n\t\t\"Basic PS256\",\n\t\t\"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9w\",\n\t\t\"PS256\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic PS384\",\n\t\t\"eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.w7-qqgj97gK4fJsq_DCqdYQiylJjzWONvD0qWWWhqEOFk2P1eDULPnqHRnjgTXoO4HAw4YIWCsZPet7nR3Xxq4ZhMqvKW8b7KlfRTb9cH8zqFvzMmybQ4jv2hKc3bXYqVow3AoR7hN_CWXI3Dv6Kd2X5xhtxRHI6IL39oTVDUQ74LACe-9t4c3QRPuj6Pq1H4FAT2E2kW_0KOc6EQhCLWEhm2Z2__OZskDC8AiPpP8Kv4k2vB7l0IKQu8Pr4RcNBlqJdq8dA5D3hk5TLxP8V5nG1Ib80MOMMqoS3FQvSLyolFX-R_jZ3-zfq6Ebsqr0yEb0AH2CfsECF7935Pa0FKQ\",\n\t\t\"PS384\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic PS512\",\n\t\t\"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.GX1HWGzFaJevuSLavqqFYaW8_TpvcjQ8KfC5fXiSDzSiT9UD9nB_ikSmDNyDILNdtjZLSvVKfXxZJqCfefxAtiozEDDdJthZ-F0uO4SPFHlGiXszvKeodh7BuTWRI2wL9-ZO4mFa8nq3GMeQAfo9cx11i7nfN8n2YNQ9SHGovG7_T_AvaMZB_jT6jkDHpwGR9mz7x1sycckEo6teLdHRnH_ZdlHlxqknmyTu8Odr5Xh0sJFOL8BepWbbvIIn-P161rRHHiDWFv6nhlHwZnVzjx7HQrWSGb6-s2cdLie9QL_8XaMcUpjLkfOMKkDOfHo6AvpL7Jbwi83Z2ZTHjJWB-A\",\n\t\t\"PS512\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\ttrue,\n\t},\n\t{\n\t\t\"basic PS256 invalid: foo => bar\",\n\t\t\"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9W\",\n\t\t\"PS256\",\n\t\tmap[string]any{\"foo\": \"bar\"},\n\t\tfalse,\n\t},\n}\n\nfunc TestRSAPSSVerify(t *testing.T) {\n\tvar err error\n\n\tkey, _ := os.ReadFile(\"test/sample_key.pub\")\n\tvar rsaPSSKey *rsa.PublicKey\n\tif rsaPSSKey, err = jwt.ParseRSAPublicKeyFromPEM(key); err != nil {\n\t\tt.Errorf(\"Unable to parse RSA public key: %v\", err)\n\t}\n\n\tfor _, data := range rsaPSSTestData {\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\terr := method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), rsaPSSKey)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestRSAPSSSign(t *testing.T) {\n\tvar err error\n\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tvar rsaPSSKey *rsa.PrivateKey\n\tif rsaPSSKey, err = jwt.ParseRSAPrivateKeyFromPEM(key); err != nil {\n\t\tt.Errorf(\"Unable to parse RSA private key: %v\", err)\n\t}\n\n\tfor _, data := range rsaPSSTestData {\n\t\tif !data.valid {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Split(data.tokenString, \".\")\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\tsig, err := method.Sign(strings.Join(parts[0:2], \".\"), rsaPSSKey)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t}\n\n\t\tssig := encodeSegment(sig)\n\t\tif ssig == parts[2] {\n\t\t\tt.Errorf(\"[%v] Signatures shouldn't match\\nnew:\\n%v\\noriginal:\\n%v\", data.name, ssig, parts[2])\n\t\t}\n\t}\n}\n\nfunc TestRSAPSSSaltLengthCompatibility(t *testing.T) {\n\t// Fails token verify, if salt length is auto.\n\tps256SaltLengthEqualsHash := &jwt.SigningMethodRSAPSS{\n\t\tSigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,\n\t\tOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthEqualsHash,\n\t\t},\n\t}\n\n\t// Behaves as before https://github.com/dgrijalva/jwt-go/issues/285 fix.\n\tps256SaltLengthAuto := &jwt.SigningMethodRSAPSS{\n\t\tSigningMethodRSA: jwt.SigningMethodPS256.SigningMethodRSA,\n\t\tOptions: &rsa.PSSOptions{\n\t\t\tSaltLength: rsa.PSSSaltLengthAuto,\n\t\t},\n\t}\n\tif !verify(t, jwt.SigningMethodPS256, makeToken(ps256SaltLengthEqualsHash)) {\n\t\tt.Error(\"SigningMethodPS256 should accept salt length that is defined in RFC\")\n\t}\n\tif !verify(t, ps256SaltLengthEqualsHash, makeToken(jwt.SigningMethodPS256)) {\n\t\tt.Error(\"Sign by SigningMethodPS256 should have salt length that is defined in RFC\")\n\t}\n\tif !verify(t, jwt.SigningMethodPS256, makeToken(ps256SaltLengthAuto)) {\n\t\tt.Error(\"SigningMethodPS256 should accept auto salt length to be compatible with previous versions\")\n\t}\n\tif !verify(t, ps256SaltLengthAuto, makeToken(jwt.SigningMethodPS256)) {\n\t\tt.Error(\"Sign by SigningMethodPS256 should be accepted by previous versions\")\n\t}\n\tif verify(t, ps256SaltLengthEqualsHash, makeToken(ps256SaltLengthAuto)) {\n\t\tt.Error(\"Auto salt length should be not accepted, when RFC salt length is required\")\n\t}\n}\n\nfunc makeToken(method jwt.SigningMethod) string {\n\ttoken := jwt.NewWithClaims(method, jwt.RegisteredClaims{\n\t\tIssuer:   \"example\",\n\t\tIssuedAt: jwt.NewNumericDate(time.Now()),\n\t})\n\tprivateKey := test.LoadRSAPrivateKeyFromDisk(\"test/sample_key\")\n\tsigned, err := token.SignedString(privateKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn signed\n}\n\nfunc verify(t *testing.T, signingMethod jwt.SigningMethod, token string) bool {\n\tsegments := strings.Split(token, \".\")\n\terr := signingMethod.Verify(strings.Join(segments[:2], \".\"), decodeSegment(t, segments[2]), test.LoadRSAPublicKeyFromDisk(\"test/sample_key.pub\"))\n\treturn err == nil\n}\n"
  },
  {
    "path": "rsa_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nvar rsaTestData = []struct {\n\tname        string\n\ttokenString string\n\talg         string\n\tvalid       bool\n}{\n\t{\n\t\t\"Basic RS256\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\t\"RS256\",\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic RS384\",\n\t\t\"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw\",\n\t\t\"RS384\",\n\t\ttrue,\n\t},\n\t{\n\t\t\"Basic RS512\",\n\t\t\"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.zBlLlmRrUxx4SJPUbV37Q1joRcI9EW13grnKduK3wtYKmDXbgDpF1cZ6B-2Jsm5RB8REmMiLpGms-EjXhgnyh2TSHE-9W2gA_jvshegLWtwRVDX40ODSkTb7OVuaWgiy9y7llvcknFBTIg-FnVPVpXMmeV_pvwQyhaz1SSwSPrDyxEmksz1hq7YONXhXPpGaNbMMeDTNP_1oj8DZaqTIL9TwV8_1wb2Odt_Fy58Ke2RVFijsOLdnyEAjt2n9Mxihu9i3PhNBkkxa2GbnXBfq3kzvZ_xxGGopLdHhJjcGWXO-NiwI9_tiu14NRv4L2xC0ItD9Yz68v2ZIZEp_DuzwRQ\",\n\t\t\"RS512\",\n\t\ttrue,\n\t},\n\t{\n\t\t\"basic invalid: foo => bar\",\n\t\t\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg\",\n\t\t\"RS256\",\n\t\tfalse,\n\t},\n}\n\nfunc TestRSAVerify(t *testing.T) {\n\tkeyData, _ := os.ReadFile(\"test/sample_key.pub\")\n\tkey, _ := jwt.ParseRSAPublicKeyFromPEM(keyData)\n\n\tfor _, data := range rsaTestData {\n\t\tparts := strings.Split(data.tokenString, \".\")\n\n\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\terr := method.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), key)\n\t\tif data.valid && err != nil {\n\t\t\tt.Errorf(\"[%v] Error while verifying key: %v\", data.name, err)\n\t\t}\n\t\tif !data.valid && err == nil {\n\t\t\tt.Errorf(\"[%v] Invalid key passed validation\", data.name)\n\t\t}\n\t}\n}\n\nfunc TestRSASign(t *testing.T) {\n\tkeyData, _ := os.ReadFile(\"test/sample_key\")\n\tkey, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)\n\n\tfor _, data := range rsaTestData {\n\t\tif data.valid {\n\t\t\tparts := strings.Split(data.tokenString, \".\")\n\t\t\tmethod := jwt.GetSigningMethod(data.alg)\n\t\t\tsig, err := method.Sign(strings.Join(parts[0:2], \".\"), key)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"[%v] Error signing token: %v\", data.name, err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(sig, decodeSegment(t, parts[2])) {\n\t\t\t\tt.Errorf(\"[%v] Incorrect signature.\\nwas:\\n%v\\nexpecting:\\n%v\", data.name, sig, parts[2])\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestRSAVerifyWithPreParsedPrivateKey(t *testing.T) {\n\tkey, _ := os.ReadFile(\"test/sample_key.pub\")\n\tparsedKey, err := jwt.ParseRSAPublicKeyFromPEM(key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttestData := rsaTestData[0]\n\tparts := strings.Split(testData.tokenString, \".\")\n\terr = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], \".\"), decodeSegment(t, parts[2]), parsedKey)\n\tif err != nil {\n\t\tt.Errorf(\"[%v] Error while verifying key: %v\", testData.name, err)\n\t}\n}\n\nfunc TestRSAWithPreParsedPrivateKey(t *testing.T) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tparsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttestData := rsaTestData[0]\n\tparts := strings.Split(testData.tokenString, \".\")\n\tsig, err := jwt.SigningMethodRS256.Sign(strings.Join(parts[0:2], \".\"), parsedKey)\n\tif err != nil {\n\t\tt.Errorf(\"[%v] Error signing token: %v\", testData.name, err)\n\t}\n\tif !reflect.DeepEqual(sig, decodeSegment(t, parts[2])) {\n\t\tt.Errorf(\"[%v] Incorrect signature.\\nwas:\\n%v\\nexpecting:\\n%v\", testData.name, sig, parts[2])\n\t}\n}\n\nfunc TestRSAKeyParsing(t *testing.T) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tsecureKey, _ := os.ReadFile(\"test/privateSecure.pem\")\n\tpubKey, _ := os.ReadFile(\"test/sample_key.pub\")\n\tbadKey := []byte(\"All your base are belong to key\")\n\n\trandomKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\tt.Errorf(\"Failed to generate RSA private key: %v\", err)\n\t}\n\n\tpublicKeyBytes := x509.MarshalPKCS1PublicKey(&randomKey.PublicKey)\n\tpkcs1Buffer := new(bytes.Buffer)\n\tif err = pem.Encode(pkcs1Buffer, &pem.Block{Type: \"RSA PUBLIC KEY\", Bytes: publicKeyBytes}); err != nil {\n\t\tt.Errorf(\"Failed to encode public pem: %v\", err)\n\t}\n\n\t// Test parsePrivateKey\n\tif _, e := jwt.ParseRSAPrivateKeyFromPEM(key); e != nil {\n\t\tt.Errorf(\"Failed to parse valid private key: %v\", e)\n\t}\n\n\tif k, e := jwt.ParseRSAPrivateKeyFromPEM(pubKey); e == nil {\n\t\tt.Errorf(\"Parsed public key as valid private key: %v\", k)\n\t}\n\n\tif k, e := jwt.ParseRSAPrivateKeyFromPEM(badKey); e == nil {\n\t\tt.Errorf(\"Parsed invalid key as valid private key: %v\", k)\n\t}\n\n\tif _, e := jwt.ParseRSAPrivateKeyFromPEMWithPassword(secureKey, \"password\"); e != nil {\n\t\tt.Errorf(\"Failed to parse valid private key with password: %v\", e)\n\t}\n\n\tif k, e := jwt.ParseRSAPrivateKeyFromPEMWithPassword(secureKey, \"123132\"); e == nil {\n\t\tt.Errorf(\"Parsed private key with invalid password %v\", k)\n\t}\n\n\t// Test parsePublicKey\n\tif _, e := jwt.ParseRSAPublicKeyFromPEM(pubKey); e != nil {\n\t\tt.Errorf(\"Failed to parse valid public key: %v\", e)\n\t}\n\n\tif k, e := jwt.ParseRSAPublicKeyFromPEM(key); e == nil {\n\t\tt.Errorf(\"Parsed private key as valid public key: %v\", k)\n\t}\n\n\tif k, e := jwt.ParseRSAPublicKeyFromPEM(badKey); e == nil {\n\t\tt.Errorf(\"Parsed invalid key as valid private key: %v\", k)\n\t}\n\n\tif _, err := jwt.ParseRSAPublicKeyFromPEM(pkcs1Buffer.Bytes()); err != nil {\n\t\tt.Errorf(\"failed to parse RSA public key: %v\", err)\n\t}\n}\n\nfunc BenchmarkRSAParsing(b *testing.B) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif _, err := jwt.ParseRSAPrivateKeyFromPEM(key); err != nil {\n\t\t\t\tb.Fatalf(\"Unable to parse RSA private key: %v\", err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkRS256Signing(b *testing.B) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tparsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tbenchmarkSigning(b, jwt.SigningMethodRS256, parsedKey)\n}\n\nfunc BenchmarkRS384Signing(b *testing.B) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tparsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tbenchmarkSigning(b, jwt.SigningMethodRS384, parsedKey)\n}\n\nfunc BenchmarkRS512Signing(b *testing.B) {\n\tkey, _ := os.ReadFile(\"test/sample_key\")\n\tparsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tbenchmarkSigning(b, jwt.SigningMethodRS512, parsedKey)\n}\n"
  },
  {
    "path": "rsa_utils.go",
    "content": "package jwt\n\nimport (\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n)\n\nvar (\n\tErrKeyMustBePEMEncoded = errors.New(\"invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key\")\n\tErrNotRSAPrivateKey    = errors.New(\"key is not a valid RSA private key\")\n\tErrNotRSAPublicKey     = errors.New(\"key is not a valid RSA public key\")\n)\n\n// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key\nfunc ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {\n\t\tif parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar pkey *rsa.PrivateKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {\n\t\treturn nil, ErrNotRSAPrivateKey\n\t}\n\n\treturn pkey, nil\n}\n\n// ParseRSAPrivateKeyFromPEMWithPassword parses a PEM encoded PKCS1 or PKCS8 private key protected with password\n//\n// Deprecated: This function is deprecated and should not be used anymore. It uses the deprecated x509.DecryptPEMBlock\n// function, which was deprecated since RFC 1423 is regarded insecure by design. Unfortunately, there is no alternative\n// in the Go standard library for now. See https://github.com/golang/go/issues/8860.\nfunc ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\tvar parsedKey any\n\n\tvar blockDecrypted []byte\n\tif blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil {\n\t\tif parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar pkey *rsa.PrivateKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {\n\t\treturn nil, ErrNotRSAPrivateKey\n\t}\n\n\treturn pkey, nil\n}\n\n// ParseRSAPublicKeyFromPEM parses a certificate or a PEM encoded PKCS1 or PKIX public key\nfunc ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {\n\tvar err error\n\n\t// Parse PEM block\n\tvar block *pem.Block\n\tif block, _ = pem.Decode(key); block == nil {\n\t\treturn nil, ErrKeyMustBePEMEncoded\n\t}\n\n\t// Parse the key\n\tvar parsedKey any\n\tif parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {\n\t\tif cert, err := x509.ParseCertificate(block.Bytes); err == nil {\n\t\t\tparsedKey = cert.PublicKey\n\t\t} else {\n\t\t\tif parsedKey, err = x509.ParsePKCS1PublicKey(block.Bytes); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\tvar pkey *rsa.PublicKey\n\tvar ok bool\n\tif pkey, ok = parsedKey.(*rsa.PublicKey); !ok {\n\t\treturn nil, ErrNotRSAPublicKey\n\t}\n\n\treturn pkey, nil\n}\n"
  },
  {
    "path": "signing_method.go",
    "content": "package jwt\n\nimport (\n\t\"sync\"\n)\n\nvar signingMethods = map[string]func() SigningMethod{}\nvar signingMethodLock = new(sync.RWMutex)\n\n// SigningMethod can be used add new methods for signing or verifying tokens. It\n// takes a decoded signature as an input in the Verify function and produces a\n// signature in Sign. The signature is then usually base64 encoded as part of a\n// JWT.\ntype SigningMethod interface {\n\tVerify(signingString string, sig []byte, key any) error // Returns nil if signature is valid\n\tSign(signingString string, key any) ([]byte, error)     // Returns signature or error\n\tAlg() string                                            // returns the alg identifier for this method (example: 'HS256')\n}\n\n// RegisterSigningMethod registers the \"alg\" name and a factory function for signing method.\n// This is typically done during init() in the method's implementation\nfunc RegisterSigningMethod(alg string, f func() SigningMethod) {\n\tsigningMethodLock.Lock()\n\tdefer signingMethodLock.Unlock()\n\n\tsigningMethods[alg] = f\n}\n\n// GetSigningMethod retrieves a signing method from an \"alg\" string\nfunc GetSigningMethod(alg string) (method SigningMethod) {\n\tsigningMethodLock.RLock()\n\tdefer signingMethodLock.RUnlock()\n\n\tif methodF, ok := signingMethods[alg]; ok {\n\t\tmethod = methodF()\n\t}\n\treturn\n}\n\n// GetAlgorithms returns a list of registered \"alg\" names\nfunc GetAlgorithms() (algs []string) {\n\tsigningMethodLock.RLock()\n\tdefer signingMethodLock.RUnlock()\n\n\tfor alg := range signingMethods {\n\t\talgs = append(algs, alg)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "staticcheck.conf",
    "content": "checks = [\"all\", \"-ST1000\", \"-ST1003\", \"-ST1016\", \"-ST1023\"]\n"
  },
  {
    "path": "test/ec256-private.pem",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIAh5qA3rmqQQuu0vbKV/+zouz/y/Iy2pLpIcWUSyImSwoAoGCCqGSM49\nAwEHoUQDQgAEYD54V/vp+54P9DXarYqx4MPcm+HKRIQzNasYSoRQHQ/6S6Ps8tpM\ncT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "test/ec256-public.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK\nRIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "test/ec384-private.pem",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDCaCvMHKhcG/qT7xsNLYnDT7sE/D+TtWIol1ROdaK1a564vx5pHbsRy\nSEKcIxISi1igBwYFK4EEACKhZANiAATYa7rJaU7feLMqrAx6adZFNQOpaUH/Uylb\nZLriOLON5YFVwtVUpO1FfEXZUIQpptRPtc5ixIPY658yhBSb6irfIJUSP9aYTflJ\nGKk/mDkK4t8mWBzhiD5B6jg9cEGhGgA=\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "test/ec384-public.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2Gu6yWlO33izKqwMemnWRTUDqWlB/1Mp\nW2S64jizjeWBVcLVVKTtRXxF2VCEKabUT7XOYsSD2OufMoQUm+oq3yCVEj/WmE35\nSRipP5g5CuLfJlgc4Yg+Qeo4PXBBoRoA\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "test/ec512-private.pem",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMIHcAgEBBEIB0pE4uFaWRx7t03BsYlYvF1YvKaBGyvoakxnodm9ou0R9wC+sJAjH\nQZZJikOg4SwNqgQ/hyrOuDK2oAVHhgVGcYmgBwYFK4EEACOhgYkDgYYABAAJXIuw\n12MUzpHggia9POBFYXSxaOGKGbMjIyDI+6q7wi7LMw3HgbaOmgIqFG72o8JBQwYN\n4IbXHf+f86CRY1AA2wHzbHvt6IhkCXTNxBEffa1yMUgu8n9cKKF2iLgyQKcKqW33\n8fGOw/n3Rm2Yd/EB56u2rnD29qS+nOM9eGS+gy39OQ==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "test/ec512-public.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACVyLsNdjFM6R4IImvTzgRWF0sWjh\nihmzIyMgyPuqu8IuyzMNx4G2jpoCKhRu9qPCQUMGDeCG1x3/n/OgkWNQANsB82x7\n7eiIZAl0zcQRH32tcjFILvJ/XCihdoi4MkCnCqlt9/HxjsP590ZtmHfxAeertq5w\n9vakvpzjPXhkvoMt/Tk=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "test/ed25519-private.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIEFMEZrmlYxczXKFxIlNvNGR5JQvDhTkLovJYxwQd3ua\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ed25519-public.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAWH7z6hpYqvPns2i4n9yymwvB3APhi4LyQ7iHOT6crtE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "test/examplePaddedKey-public.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIcaUjXhC7Mn2OonyfHF+zjblKkns\n4GLbILnHrZr+aQwddiff5urCDAZ177t81Mn39CDs3uhlNDxfRIRheGnK/Q==\n-----END PUBLIC KEY-----"
  },
  {
    "path": "test/helpers.go",
    "content": "package test\n\nimport (\n\t\"crypto\"\n\t\"crypto/rsa\"\n\t\"os\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nfunc LoadRSAPrivateKeyFromDisk(location string) *rsa.PrivateKey {\n\tkeyData, e := os.ReadFile(location)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\tkey, e := jwt.ParseRSAPrivateKeyFromPEM(keyData)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\treturn key\n}\n\nfunc LoadRSAPublicKeyFromDisk(location string) *rsa.PublicKey {\n\tkeyData, e := os.ReadFile(location)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\tkey, e := jwt.ParseRSAPublicKeyFromPEM(keyData)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\treturn key\n}\n\n// MakeSampleToken creates and returns a encoded JWT token that has been signed with the specified cryptographic key.\nfunc MakeSampleToken(c jwt.Claims, method jwt.SigningMethod, key any) string {\n\ttoken := jwt.NewWithClaims(method, c)\n\ts, e := token.SignedString(key)\n\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\n\treturn s\n}\n\nfunc LoadECPrivateKeyFromDisk(location string) crypto.PrivateKey {\n\tkeyData, e := os.ReadFile(location)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\tkey, e := jwt.ParseECPrivateKeyFromPEM(keyData)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\treturn key\n}\n\nfunc LoadECPublicKeyFromDisk(location string) crypto.PublicKey {\n\tkeyData, e := os.ReadFile(location)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\tkey, e := jwt.ParseECPublicKeyFromPEM(keyData)\n\tif e != nil {\n\t\tpanic(e.Error())\n\t}\n\treturn key\n}\n"
  },
  {
    "path": "test/hmacTestKey",
    "content": "\u0003#5K+\u000f~\u0006ew{Z(T(\u000fP.Z\u0006Gwb=\"=.!r\u0005.O\b͚gЀ"
  },
  {
    "path": "test/privateSecure.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,7487BB8910A3741B\n\niL7m48mbFSIy1Y5xbXWwPTR07ufxu7o+myGUE+AdDeWWISkd5W6Gl44oX/jgXldS\nmL/ntUXoZzQz2WKEYLwssAtSTGF+QgSIMvV5faiP+pLYvWgk0oVr42po00CvADFL\neDAJC7LgagYifS1l4EAK4MY8RGCHyJWEN5JAr0fc/Haa3WfWZ009kOWAp8MDuYxB\nhQlCKUmnUpXCp5c6jwbjlyinLj8XwzzjZ/rVRsY+t2Z0Vcd5qzR5BV8IJCqbG5Py\nz15/EFgMG2N2eYMsiEKgdXeKW2H5XIoWyun/3pBigWaDnTtiWSt9kz2MplqYfIT7\nF+0XE3gdDGalAeN3YwFPHCkxxBmcI+s6lQG9INmf2/gkJQ+MOZBVXKmGLv6Qis3l\n0eyUz1yZvNzf0zlcUBjiPulLF3peThHMEzhSsATfPomyg5NJ0X7ttd0ybnq+sPe4\nqg2OJ8qNhYrqnx7Xlvj61+B2NAZVHvIioma1FzqX8DxQYrnR5S6DJExDqvzNxEz6\n5VPQlH2Ig4hTvNzla84WgJ6USc/2SS4ehCReiNvfeNG9sPZKQnr/Ss8KPIYsKGcC\nPz/vEqbWDmJwHb7KixCQKPt1EbD+/uf0YnhskOWM15YiFbYAOZKJ5rcbz2Zu66vg\nGAmqcBsHeFR3s/bObEzjxOmMfSr1vzvr4ActNJWVtfNKZNobSehZiMSHL54AXAZW\nYj48pwTbf7b1sbF0FeCuwTFiYxM+yiZVO5ciYOfmo4HUg53PjknKpcKtEFSj02P1\n8JRBSb++V0IeMDyZLl12zgURDsvualbJMMBBR8emIpF13h0qdyah431gDhHGBnnC\nJ5UDGq21/flFjzz0x/Okjwf7mPK5pcmF+uW7AxtHqws6m93yD5+RFmfZ8cb/8CL8\njmsQslj+OIE64ykkRoJWpNBKyQjL3CnPnLmAB6TQKxegR94C7/hP1FvRW+W0AgZy\ng2QczKQU3KBQP18Ui1HTbkOUJT0Lsy4FnmJFCB/STPRo6NlJiATKHq/cqHWQUvZd\nd4oTMb1opKfs7AI9wiJBuskpGAECdRnVduml3dT4p//3BiP6K9ImWMSJeFpjFAFs\nAbBMKyitMs0Fyn9AJRPl23TKVQ3cYeSTxus4wLmx5ECSsHRV6g06nYjBp4GWEqSX\nRVclXF3zmy3b1+O5s2chJN6TrypzYSEYXJb1vvQLK0lNXqwxZAFV7Roi6xSG0fSY\nEAtdUifLonu43EkrLh55KEwkXdVV8xneUjh+TF8VgJKMnqDFfeHFdmN53YYh3n3F\nkpYSmVLRzQmLbH9dY+7kqvnsQm8y76vjug3p4IbEbHp/fNGf+gv7KDng1HyCl9A+\nOw/Hlr0NqCAIhminScbRsZ4SgbRTRgGEYZXvyOtQa/uL6I8t2NR4W7ynispMs0QL\nRD61i3++bQXuTi4i8dg3yqIfe9S22NHSzZY/lAHAmmc3r5NrQ1TM1hsSxXawT5CU\nanWFjbH6YQ/QplkkAqZMpropWn6ZdNDg/+BUjukDs0HZrbdGy846WxQUvE7G2bAw\nIFQ1SymBZBtfnZXhfAXOHoWh017p6HsIkb2xmFrigMj7Jh10VVhdWg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/sample_key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn\nSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i\ncqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC\nPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR\nap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA\nRdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3\nn6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy\nMaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9\nPOIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE\nKdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM\nIvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn\nFcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY\nmEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj\nFuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U\nI5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs\n2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn\n/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT\nOvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86\nEunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+\nhR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0\n4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb\nmDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry\neBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3\nCKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+\n9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/sample_key.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41\nfGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7\nmCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp\nHssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2\nXrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b\nODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy\n7wIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "token.go",
    "content": "package jwt\n\nimport (\n\t\"crypto\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n)\n\n// Keyfunc will be used by the Parse methods as a callback function to supply\n// the key for verification.  The function receives the parsed, but unverified\n// Token.  This allows you to use properties in the Header of the token (such as\n// `kid`) to identify which key to use.\n//\n// The returned any may be a single key or a VerificationKeySet containing\n// multiple keys.\ntype Keyfunc func(*Token) (any, error)\n\n// VerificationKey represents a public or secret key for verifying a token's signature.\ntype VerificationKey interface {\n\tcrypto.PublicKey | []uint8\n}\n\n// VerificationKeySet is a set of public or secret keys. It is used by the parser to verify a token.\ntype VerificationKeySet struct {\n\tKeys []VerificationKey\n}\n\n// Token represents a JWT Token.  Different fields will be used depending on\n// whether you're creating or parsing/verifying a token.\ntype Token struct {\n\tRaw       string         // Raw contains the raw token.  Populated when you [Parse] a token\n\tMethod    SigningMethod  // Method is the signing method used or to be used\n\tHeader    map[string]any // Header is the first segment of the token in decoded form\n\tClaims    Claims         // Claims is the second segment of the token in decoded form\n\tSignature []byte         // Signature is the third segment of the token in decoded form.  Populated when you [Parse] or sign a token\n\tValid     bool           // Valid specifies if the token is valid.  Populated when you [Parse] a token\n}\n\n// New creates a new [Token] with the specified signing method and an empty map\n// of claims. Additional options can be specified, but are currently unused.\nfunc New(method SigningMethod, opts ...TokenOption) *Token {\n\treturn NewWithClaims(method, MapClaims{}, opts...)\n}\n\n// NewWithClaims creates a new [Token] with the specified signing method and\n// claims. Additional options can be specified, but are currently unused.\nfunc NewWithClaims(method SigningMethod, claims Claims, opts ...TokenOption) *Token {\n\treturn &Token{\n\t\tHeader: map[string]any{\n\t\t\t\"typ\": \"JWT\",\n\t\t\t\"alg\": method.Alg(),\n\t\t},\n\t\tClaims: claims,\n\t\tMethod: method,\n\t}\n}\n\n// SignedString creates and returns a complete, signed JWT. The token is signed\n// using the SigningMethod specified in the token. Please refer to\n// https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types\n// for an overview of the different signing methods and their respective key\n// types.\nfunc (t *Token) SignedString(key any) (string, error) {\n\tsstr, err := t.SigningString()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tsig, err := t.Method.Sign(sstr, key)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tt.Signature = sig\n\n\treturn sstr + \".\" + t.EncodeSegment(sig), nil\n}\n\n// SigningString generates the signing string.  This is the most expensive part\n// of the whole deal. Unless you need this for something special, just go\n// straight for the SignedString.\nfunc (t *Token) SigningString() (string, error) {\n\th, err := json.Marshal(t.Header)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tc, err := json.Marshal(t.Claims)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn t.EncodeSegment(h) + \".\" + t.EncodeSegment(c), nil\n}\n\n// EncodeSegment encodes a JWT specific base64url encoding with padding\n// stripped. In the future, this function might take into account a\n// [TokenOption]. Therefore, this function exists as a method of [Token], rather\n// than a global function.\nfunc (*Token) EncodeSegment(seg []byte) string {\n\treturn base64.RawURLEncoding.EncodeToString(seg)\n}\n"
  },
  {
    "path": "token_option.go",
    "content": "package jwt\n\n// TokenOption is a reserved type, which provides some forward compatibility,\n// if we ever want to introduce token creation-related options.\ntype TokenOption func(*Token)\n"
  },
  {
    "path": "token_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nfunc TestToken_SigningString(t1 *testing.T) {\n\ttype fields struct {\n\t\tRaw       string\n\t\tMethod    jwt.SigningMethod\n\t\tHeader    map[string]any\n\t\tClaims    jwt.Claims\n\t\tSignature []byte\n\t\tValid     bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"\",\n\t\t\tfields: fields{\n\t\t\t\tRaw:    \"\",\n\t\t\t\tMethod: jwt.SigningMethodHS256,\n\t\t\t\tHeader: map[string]any{\n\t\t\t\t\t\"typ\": \"JWT\",\n\t\t\t\t\t\"alg\": jwt.SigningMethodHS256.Alg(),\n\t\t\t\t},\n\t\t\t\tClaims: jwt.RegisteredClaims{},\n\t\t\t\tValid:  false,\n\t\t\t},\n\t\t\twant:    \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30\",\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt1.Run(tt.name, func(t1 *testing.T) {\n\t\t\tt := &jwt.Token{\n\t\t\t\tRaw:       tt.fields.Raw,\n\t\t\t\tMethod:    tt.fields.Method,\n\t\t\t\tHeader:    tt.fields.Header,\n\t\t\t\tClaims:    tt.fields.Claims,\n\t\t\t\tSignature: tt.fields.Signature,\n\t\t\t\tValid:     tt.fields.Valid,\n\t\t\t}\n\t\t\tgot, err := t.SigningString()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt1.Errorf(\"SigningString() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt1.Errorf(\"SigningString() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkToken_SigningString(b *testing.B) {\n\tt := &jwt.Token{\n\t\tMethod: jwt.SigningMethodHS256,\n\t\tHeader: map[string]any{\n\t\t\t\"typ\": \"JWT\",\n\t\t\t\"alg\": jwt.SigningMethodHS256.Alg(),\n\t\t},\n\t\tClaims: jwt.RegisteredClaims{},\n\t}\n\tb.Run(\"BenchmarkToken_SigningString\", func(b *testing.B) {\n\t\tb.ResetTimer()\n\t\tb.ReportAllocs()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\t_, _ = t.SigningString()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "types.go",
    "content": "package jwt\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// TimePrecision sets the precision of times and dates within this library. This\n// has an influence on the precision of times when comparing expiry or other\n// related time fields. Furthermore, it is also the precision of times when\n// serializing.\n//\n// For backwards compatibility the default precision is set to seconds, so that\n// no fractional timestamps are generated.\nvar TimePrecision = time.Second\n\n// MarshalSingleStringAsArray modifies the behavior of the ClaimStrings type,\n// especially its MarshalJSON function.\n//\n// If it is set to true (the default), it will always serialize the type as an\n// array of strings, even if it just contains one element, defaulting to the\n// behavior of the underlying []string. If it is set to false, it will serialize\n// to a single string, if it contains one element. Otherwise, it will serialize\n// to an array of strings.\nvar MarshalSingleStringAsArray = true\n\n// NumericDate represents a JSON numeric date value, as referenced at\n// https://datatracker.ietf.org/doc/html/rfc7519#section-2.\ntype NumericDate struct {\n\ttime.Time\n}\n\n// NewNumericDate constructs a new *NumericDate from a standard library time.Time struct.\n// It will truncate the timestamp according to the precision specified in TimePrecision.\nfunc NewNumericDate(t time.Time) *NumericDate {\n\treturn &NumericDate{t.Truncate(TimePrecision)}\n}\n\n// newNumericDateFromSeconds creates a new *NumericDate out of a float64 representing a\n// UNIX epoch with the float fraction representing non-integer seconds.\nfunc newNumericDateFromSeconds(f float64) *NumericDate {\n\tround, frac := math.Modf(f)\n\treturn NewNumericDate(time.Unix(int64(round), int64(frac*1e9)))\n}\n\n// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch\n// represented in NumericDate to a byte array, using the precision specified in TimePrecision.\nfunc (date NumericDate) MarshalJSON() (b []byte, err error) {\n\tvar prec int\n\tif TimePrecision < time.Second {\n\t\tprec = int(math.Log10(float64(time.Second) / float64(TimePrecision)))\n\t}\n\ttruncatedDate := date.Truncate(TimePrecision)\n\n\t// For very large timestamps, UnixNano would overflow an int64, but this\n\t// function requires nanosecond level precision, so we have to use the\n\t// following technique to get round the issue:\n\t//\n\t// 1. Take the normal unix timestamp to form the whole number part of the\n\t//    output,\n\t// 2. Take the result of the Nanosecond function, which returns the offset\n\t//    within the second of the particular unix time instance, to form the\n\t//    decimal part of the output\n\t// 3. Concatenate them to produce the final result\n\tseconds := strconv.FormatInt(truncatedDate.Unix(), 10)\n\tnanosecondsOffset := strconv.FormatFloat(float64(truncatedDate.Nanosecond())/float64(time.Second), 'f', prec, 64)\n\n\toutput := append([]byte(seconds), []byte(nanosecondsOffset)[1:]...)\n\n\treturn output, nil\n}\n\n// UnmarshalJSON is an implementation of the json.RawMessage interface and\n// deserializes a [NumericDate] from a JSON representation, i.e. a\n// [json.Number]. This number represents an UNIX epoch with either integer or\n// non-integer seconds.\nfunc (date *NumericDate) UnmarshalJSON(b []byte) (err error) {\n\tvar (\n\t\tnumber json.Number\n\t\tf      float64\n\t)\n\n\tif err = json.Unmarshal(b, &number); err != nil {\n\t\treturn fmt.Errorf(\"could not parse NumericData: %w\", err)\n\t}\n\n\tif f, err = number.Float64(); err != nil {\n\t\treturn fmt.Errorf(\"could not convert json number value to float: %w\", err)\n\t}\n\n\tn := newNumericDateFromSeconds(f)\n\t*date = *n\n\n\treturn nil\n}\n\n// ClaimStrings is basically just a slice of strings, but it can be either\n// serialized from a string array or just a string. This type is necessary,\n// since the \"aud\" claim can either be a single string or an array.\ntype ClaimStrings []string\n\nfunc (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) {\n\tvar value any\n\n\tif err = json.Unmarshal(data, &value); err != nil {\n\t\treturn err\n\t}\n\n\tvar aud []string\n\n\tswitch v := value.(type) {\n\tcase string:\n\t\taud = append(aud, v)\n\tcase []string:\n\t\taud = ClaimStrings(v)\n\tcase []any:\n\t\tfor _, vv := range v {\n\t\t\tvs, ok := vv.(string)\n\t\t\tif !ok {\n\t\t\t\treturn ErrInvalidType\n\t\t\t}\n\t\t\taud = append(aud, vs)\n\t\t}\n\tcase nil:\n\t\treturn nil\n\tdefault:\n\t\treturn ErrInvalidType\n\t}\n\n\t*s = aud\n\n\treturn\n}\n\nfunc (s ClaimStrings) MarshalJSON() (b []byte, err error) {\n\t// This handles a special case in the JWT RFC. If the string array, e.g.\n\t// used by the \"aud\" field, only contains one element, it MAY be serialized\n\t// as a single string. This may or may not be desired based on the ecosystem\n\t// of other JWT library used, so we make it configurable by the variable\n\t// MarshalSingleStringAsArray.\n\tif len(s) == 1 && !MarshalSingleStringAsArray {\n\t\treturn json.Marshal(s[0])\n\t}\n\n\treturn json.Marshal([]string(s))\n}\n"
  },
  {
    "path": "types_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nfunc TestNumericDate(t *testing.T) {\n\tvar s struct {\n\t\tIat jwt.NumericDate `json:\"iat\"`\n\t\tExp jwt.NumericDate `json:\"exp\"`\n\t}\n\n\toldPrecision := jwt.TimePrecision\n\n\tjwt.TimePrecision = time.Microsecond\n\n\traw := `{\"iat\":1516239022.000000,\"exp\":1516239022.123450}`\n\n\tif err := json.Unmarshal([]byte(raw), &s); err != nil {\n\t\tt.Fatalf(\"Unexpected error: %s\", err)\n\t}\n\n\tb, _ := json.Marshal(s)\n\n\tif raw != string(b) {\n\t\tt.Errorf(\"Serialized format of numeric date mismatch. Expecting: %s  Got: %s\", raw, string(b))\n\t}\n\n\tjwt.TimePrecision = oldPrecision\n}\n\nfunc TestSingleArrayMarshal(t *testing.T) {\n\tjwt.MarshalSingleStringAsArray = false\n\n\ts := jwt.ClaimStrings{\"test\"}\n\texpected := `\"test\"`\n\n\tb, err := json.Marshal(s)\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error: %s\", err)\n\t}\n\n\tif expected != string(b) {\n\t\tt.Errorf(\"Serialized format of string array mismatch. Expecting: %s  Got: %s\", expected, string(b))\n\t}\n\n\tjwt.MarshalSingleStringAsArray = true\n\n\texpected = `[\"test\"]`\n\n\tb, err = json.Marshal(s)\n\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error: %s\", err)\n\t}\n\n\tif expected != string(b) {\n\t\tt.Errorf(\"Serialized format of string array mismatch. Expecting: %s  Got: %s\", expected, string(b))\n\t}\n}\n\nfunc TestNumericDate_MarshalJSON(t *testing.T) {\n\t// Do not run this test in parallel because it's changing\n\t// global state.\n\toldPrecision := jwt.TimePrecision\n\tt.Cleanup(func() {\n\t\tjwt.TimePrecision = oldPrecision\n\t})\n\n\ttt := []struct {\n\t\tin        time.Time\n\t\twant      string\n\t\tprecision time.Duration\n\t}{\n\t\t{time.Unix(5243700879, 0), \"5243700879\", time.Second},\n\t\t{time.Unix(5243700879, 0), \"5243700879.000\", time.Millisecond},\n\t\t{time.Unix(5243700879, 0), \"5243700879.000000\", time.Microsecond},\n\t\t{time.Unix(5243700879, 0), \"5243700879.000000000\", time.Nanosecond},\n\t\t//\n\t\t{time.Unix(4239425898, 0), \"4239425898\", time.Second},\n\t\t{time.Unix(4239425898, 0), \"4239425898.000\", time.Millisecond},\n\t\t{time.Unix(4239425898, 0), \"4239425898.000000\", time.Microsecond},\n\t\t{time.Unix(4239425898, 0), \"4239425898.000000000\", time.Nanosecond},\n\t\t//\n\t\t{time.Unix(253402271999, 0), \"253402271999\", time.Second},\n\t\t{time.Unix(253402271999, 0), \"253402271999.000\", time.Millisecond},\n\t\t{time.Unix(253402271999, 0), \"253402271999.000000\", time.Microsecond},\n\t\t{time.Unix(253402271999, 0), \"253402271999.000000000\", time.Nanosecond},\n\t\t//\n\t\t{time.Unix(0, 1644285000210402000), \"1644285000\", time.Second},\n\t\t{time.Unix(0, 1644285000210402000), \"1644285000.210\", time.Millisecond},\n\t\t{time.Unix(0, 1644285000210402000), \"1644285000.210402\", time.Microsecond},\n\t\t{time.Unix(0, 1644285000210402000), \"1644285000.210402000\", time.Nanosecond},\n\t\t//\n\t\t{time.Unix(0, 1644285315063096000), \"1644285315\", time.Second},\n\t\t{time.Unix(0, 1644285315063096000), \"1644285315.063\", time.Millisecond},\n\t\t{time.Unix(0, 1644285315063096000), \"1644285315.063096\", time.Microsecond},\n\t\t{time.Unix(0, 1644285315063096000), \"1644285315.063096000\", time.Nanosecond},\n\t\t// Maximum time that a go time.Time can represent\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775807\", time.Second},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775807.999\", time.Millisecond},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775807.999999\", time.Microsecond},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775807.999999999\", time.Nanosecond},\n\t\t// Strange precisions\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775807\", time.Second},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854775756\", time.Minute},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854774016\", time.Hour},\n\t\t{time.Unix(math.MaxInt64, 999999999), \"9223372036854745216\", 24 * time.Hour},\n\t}\n\n\tfor i, tc := range tt {\n\t\tjwt.TimePrecision = tc.precision\n\t\tby, err := jwt.NewNumericDate(tc.in).MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif got := string(by); got != tc.want {\n\t\t\tt.Errorf(\"[%d]: failed encoding: got %q want %q\", i, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestGetSignatureAfterSigning(t *testing.T) {\n\ttoken := jwt.New(jwt.SigningMethodHS256, nil)\n\tsignedString, err := token.SignedString([]byte(\"test12345\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsigStr := signedString[strings.LastIndex(signedString, \".\")+1:]\n\tsig, err := base64.RawURLEncoding.DecodeString(sigStr)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !bytes.Equal(sig, token.Signature) {\n\t\tt.Errorf(\"token.Signature not equal to signature in signed string\")\n\t}\n}\n"
  },
  {
    "path": "validator.go",
    "content": "package jwt\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n)\n\n// ClaimsValidator is an interface that can be implemented by custom claims who\n// wish to execute any additional claims validation based on\n// application-specific logic. The Validate function is then executed in\n// addition to the regular claims validation and any error returned is appended\n// to the final validation result.\n//\n//\ttype MyCustomClaims struct {\n//\t    Foo string `json:\"foo\"`\n//\t    jwt.RegisteredClaims\n//\t}\n//\n//\tfunc (m MyCustomClaims) Validate() error {\n//\t    if m.Foo != \"bar\" {\n//\t        return errors.New(\"must be foobar\")\n//\t    }\n//\t    return nil\n//\t}\ntype ClaimsValidator interface {\n\tClaims\n\tValidate() error\n}\n\n// Validator is the core of the new Validation API. It is automatically used by\n// a [Parser] during parsing and can be modified with various parser options.\n//\n// The [NewValidator] function should be used to create an instance of this\n// struct.\ntype Validator struct {\n\t// leeway is an optional leeway that can be provided to account for clock skew.\n\tleeway time.Duration\n\n\t// timeFunc is used to supply the current time that is needed for\n\t// validation. If unspecified, this defaults to time.Now.\n\ttimeFunc func() time.Time\n\n\t// requireExp specifies whether the exp claim is required\n\trequireExp bool\n\n\t// requireNbf specifies whether the nbf claim is required\n\trequireNbf bool\n\n\t// verifyIat specifies whether the iat (Issued At) claim will be verified.\n\t// According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this\n\t// only specifies the age of the token, but no validation check is\n\t// necessary. However, if wanted, it can be checked if the iat is\n\t// unrealistic, i.e., in the future.\n\tverifyIat bool\n\n\t// expectedAud contains the audience this token expects. Supplying an empty\n\t// slice will disable aud checking.\n\texpectedAud []string\n\n\t// expectAllAud specifies whether all expected audiences must be present in\n\t// the token. If false, only one of the expected audiences must be present.\n\texpectAllAud bool\n\n\t// expectedIss contains the issuer this token expects. Supplying an empty\n\t// string will disable iss checking.\n\texpectedIss string\n\n\t// expectedSub contains the subject this token expects. Supplying an empty\n\t// string will disable sub checking.\n\texpectedSub string\n}\n\n// NewValidator can be used to create a stand-alone validator with the supplied\n// options. This validator can then be used to validate already parsed claims.\n//\n// Note: Under normal circumstances, explicitly creating a validator is not\n// needed and can potentially be dangerous; instead functions of the [Parser]\n// class should be used.\n//\n// The [Validator] is only checking the *validity* of the claims, such as its\n// expiration time, but it does NOT perform *signature verification* of the\n// token.\nfunc NewValidator(opts ...ParserOption) *Validator {\n\tp := NewParser(opts...)\n\treturn p.validator\n}\n\n// Validate validates the given claims. It will also perform any custom\n// validation if claims implements the [ClaimsValidator] interface.\n//\n// Note: It will NOT perform any *signature verification* on the token that\n// contains the claims and expects that the [Claim] was already successfully\n// verified.\nfunc (v *Validator) Validate(claims Claims) error {\n\tvar (\n\t\tnow  time.Time\n\t\terrs = make([]error, 0, 6)\n\t\terr  error\n\t)\n\n\t// Check, if we have a time func\n\tif v.timeFunc != nil {\n\t\tnow = v.timeFunc()\n\t} else {\n\t\tnow = time.Now()\n\t}\n\n\t// We always need to check the expiration time, but usage of the claim\n\t// itself is OPTIONAL by default. requireExp overrides this behavior\n\t// and makes the exp claim mandatory.\n\tif err = v.verifyExpiresAt(claims, now, v.requireExp); err != nil {\n\t\terrs = append(errs, err)\n\t}\n\n\t// We always need to check not-before, but usage of the claim itself is\n\t// OPTIONAL by default. requireNbf overrides this behavior and makes\n\t// the nbf claim mandatory.\n\tif err = v.verifyNotBefore(claims, now, v.requireNbf); err != nil {\n\t\terrs = append(errs, err)\n\t}\n\n\t// Check issued-at if the option is enabled\n\tif v.verifyIat {\n\t\tif err = v.verifyIssuedAt(claims, now, false); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\t// If we have an expected audience, we also require the audience claim\n\tif len(v.expectedAud) > 0 {\n\t\tif err = v.verifyAudience(claims, v.expectedAud, v.expectAllAud); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\t// If we have an expected issuer, we also require the issuer claim\n\tif v.expectedIss != \"\" {\n\t\tif err = v.verifyIssuer(claims, v.expectedIss, true); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\t// If we have an expected subject, we also require the subject claim\n\tif v.expectedSub != \"\" {\n\t\tif err = v.verifySubject(claims, v.expectedSub, true); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\t// Finally, we want to give the claim itself some possibility to do some\n\t// additional custom validation based on a custom Validate function.\n\tcvt, ok := claims.(ClaimsValidator)\n\tif ok {\n\t\tif err := cvt.Validate(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\tif len(errs) == 0 {\n\t\treturn nil\n\t}\n\n\treturn joinErrors(errs...)\n}\n\n// verifyExpiresAt compares the exp claim in claims against cmp. This function\n// will succeed if cmp < exp. Additional leeway is taken into account.\n//\n// If exp is not set, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error {\n\texp, err := claims.GetExpirationTime()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif exp == nil {\n\t\treturn errorIfRequired(required, \"exp\")\n\t}\n\n\treturn errorIfFalse(cmp.Before((exp.Time).Add(+v.leeway)), ErrTokenExpired)\n}\n\n// verifyIssuedAt compares the iat claim in claims against cmp. This function\n// will succeed if cmp >= iat. Additional leeway is taken into account.\n//\n// If iat is not set, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error {\n\tiat, err := claims.GetIssuedAt()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif iat == nil {\n\t\treturn errorIfRequired(required, \"iat\")\n\t}\n\n\treturn errorIfFalse(!cmp.Before(iat.Add(-v.leeway)), ErrTokenUsedBeforeIssued)\n}\n\n// verifyNotBefore compares the nbf claim in claims against cmp. This function\n// will return true if cmp >= nbf. Additional leeway is taken into account.\n//\n// If nbf is not set, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error {\n\tnbf, err := claims.GetNotBefore()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif nbf == nil {\n\t\treturn errorIfRequired(required, \"nbf\")\n\t}\n\n\treturn errorIfFalse(!cmp.Before(nbf.Add(-v.leeway)), ErrTokenNotValidYet)\n}\n\n// verifyAudience compares the aud claim against cmp.\n//\n// If aud is not set or an empty list, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifyAudience(claims Claims, cmp []string, expectAllAud bool) error {\n\taud, err := claims.GetAudience()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Check that aud exists and is not empty. We only require the aud claim\n\t// if we expect at least one audience to be present.\n\tif len(aud) == 0 || len(aud) == 1 && aud[0] == \"\" {\n\t\trequired := len(v.expectedAud) > 0\n\t\treturn errorIfRequired(required, \"aud\")\n\t}\n\n\tif !expectAllAud {\n\t\tfor _, a := range aud {\n\t\t\t// If we only expect one match, we can stop early if we find a match\n\t\t\tif slices.Contains(cmp, a) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\treturn ErrTokenInvalidAudience\n\t}\n\n\t// Note that we are looping cmp here to ensure that all expected audiences\n\t// are present in the aud claim.\n\tfor _, a := range cmp {\n\t\tif !slices.Contains(aud, a) {\n\t\t\treturn ErrTokenInvalidAudience\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// verifyIssuer compares the iss claim in claims against cmp.\n//\n// If iss is not set, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifyIssuer(claims Claims, cmp string, required bool) error {\n\tiss, err := claims.GetIssuer()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif iss == \"\" {\n\t\treturn errorIfRequired(required, \"iss\")\n\t}\n\n\treturn errorIfFalse(iss == cmp, ErrTokenInvalidIssuer)\n}\n\n// verifySubject compares the sub claim against cmp.\n//\n// If sub is not set, it will succeed if the claim is not required,\n// otherwise ErrTokenRequiredClaimMissing will be returned.\n//\n// Additionally, if any error occurs while retrieving the claim, e.g., when its\n// the wrong type, an ErrTokenUnverifiable error will be returned.\nfunc (v *Validator) verifySubject(claims Claims, cmp string, required bool) error {\n\tsub, err := claims.GetSubject()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif sub == \"\" {\n\t\treturn errorIfRequired(required, \"sub\")\n\t}\n\n\treturn errorIfFalse(sub == cmp, ErrTokenInvalidSubject)\n}\n\n// errorIfFalse returns the error specified in err, if the value is true.\n// Otherwise, nil is returned.\nfunc errorIfFalse(value bool, err error) error {\n\tif value {\n\t\treturn nil\n\t} else {\n\t\treturn err\n\t}\n}\n\n// errorIfRequired returns an ErrTokenRequiredClaimMissing error if required is\n// true. Otherwise, nil is returned.\nfunc errorIfRequired(required bool, claim string) error {\n\tif required {\n\t\treturn newError(fmt.Sprintf(\"%s claim is required\", claim), ErrTokenRequiredClaimMissing)\n\t} else {\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "validator_test.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar ErrFooBar = errors.New(\"must be foobar\")\n\ntype MyCustomClaims struct {\n\tFoo string `json:\"foo\"`\n\tRegisteredClaims\n}\n\nfunc (m MyCustomClaims) Validate() error {\n\tif m.Foo != \"bar\" {\n\t\treturn ErrFooBar\n\t}\n\treturn nil\n}\n\nfunc Test_Validator_Validate(t *testing.T) {\n\ttype fields struct {\n\t\tleeway       time.Duration\n\t\ttimeFunc     func() time.Time\n\t\tverifyIat    bool\n\t\texpectedAud  []string\n\t\texpectAllAud bool\n\t\texpectedIss  string\n\t\texpectedSub  string\n\t}\n\ttype args struct {\n\t\tclaims Claims\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"expected iss mismatch\",\n\t\t\tfields:  fields{expectedIss: \"me\"},\n\t\t\targs:    args{RegisteredClaims{Issuer: \"not_me\"}},\n\t\t\twantErr: ErrTokenInvalidIssuer,\n\t\t},\n\t\t{\n\t\t\tname:    \"expected iss is missing\",\n\t\t\tfields:  fields{expectedIss: \"me\"},\n\t\t\targs:    args{RegisteredClaims{}},\n\t\t\twantErr: ErrTokenRequiredClaimMissing,\n\t\t},\n\t\t{\n\t\t\tname:    \"expected sub mismatch\",\n\t\t\tfields:  fields{expectedSub: \"me\"},\n\t\t\targs:    args{RegisteredClaims{Subject: \"not-me\"}},\n\t\t\twantErr: ErrTokenInvalidSubject,\n\t\t},\n\t\t{\n\t\t\tname:    \"expected sub is missing\",\n\t\t\tfields:  fields{expectedSub: \"me\"},\n\t\t\targs:    args{RegisteredClaims{}},\n\t\t\twantErr: ErrTokenRequiredClaimMissing,\n\t\t},\n\t\t{\n\t\t\tname:    \"custom validator\",\n\t\t\tfields:  fields{},\n\t\t\targs:    args{MyCustomClaims{Foo: \"not-bar\"}},\n\t\t\twantErr: ErrFooBar,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\tleeway:       tt.fields.leeway,\n\t\t\t\ttimeFunc:     tt.fields.timeFunc,\n\t\t\t\tverifyIat:    tt.fields.verifyIat,\n\t\t\t\texpectedAud:  tt.fields.expectedAud,\n\t\t\t\texpectAllAud: tt.fields.expectAllAud,\n\t\t\t\texpectedIss:  tt.fields.expectedIss,\n\t\t\t\texpectedSub:  tt.fields.expectedSub,\n\t\t\t}\n\t\t\tif err := v.Validate(tt.args.claims); (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.Validate() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_verifyExpiresAt(t *testing.T) {\n\ttype fields struct {\n\t\tleeway   time.Duration\n\t\ttimeFunc func() time.Time\n\t}\n\ttype args struct {\n\t\tclaims   Claims\n\t\tcmp      time.Time\n\t\trequired bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"good claim\",\n\t\t\tfields:  fields{timeFunc: time.Now},\n\t\t\targs:    args{claims: RegisteredClaims{ExpiresAt: NewNumericDate(time.Now().Add(10 * time.Minute))}},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"claims with invalid type\",\n\t\t\tfields:  fields{},\n\t\t\targs:    args{claims: MapClaims{\"exp\": \"string\"}},\n\t\t\twantErr: ErrInvalidType,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\tleeway:   tt.fields.leeway,\n\t\t\t\ttimeFunc: tt.fields.timeFunc,\n\t\t\t}\n\n\t\t\terr := v.verifyExpiresAt(tt.args.claims, tt.args.cmp, tt.args.required)\n\t\t\tif (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.verifyExpiresAt() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_verifyIssuer(t *testing.T) {\n\ttype fields struct {\n\t\texpectedIss string\n\t}\n\ttype args struct {\n\t\tclaims   Claims\n\t\tcmp      string\n\t\trequired bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"good claim\",\n\t\t\tfields:  fields{expectedIss: \"me\"},\n\t\t\targs:    args{claims: MapClaims{\"iss\": \"me\"}, cmp: \"me\"},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"claims with invalid type\",\n\t\t\tfields:  fields{expectedIss: \"me\"},\n\t\t\targs:    args{claims: MapClaims{\"iss\": 1}, cmp: \"me\"},\n\t\t\twantErr: ErrInvalidType,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\texpectedIss: tt.fields.expectedIss,\n\t\t\t}\n\t\t\terr := v.verifyIssuer(tt.args.claims, tt.args.cmp, tt.args.required)\n\t\t\tif (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.verifyIssuer() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_verifySubject(t *testing.T) {\n\ttype fields struct {\n\t\texpectedSub string\n\t}\n\ttype args struct {\n\t\tclaims   Claims\n\t\tcmp      string\n\t\trequired bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"good claim\",\n\t\t\tfields:  fields{expectedSub: \"me\"},\n\t\t\targs:    args{claims: MapClaims{\"sub\": \"me\"}, cmp: \"me\"},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"claims with invalid type\",\n\t\t\tfields:  fields{expectedSub: \"me\"},\n\t\t\targs:    args{claims: MapClaims{\"sub\": 1}, cmp: \"me\"},\n\t\t\twantErr: ErrInvalidType,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\texpectedSub: tt.fields.expectedSub,\n\t\t\t}\n\t\t\terr := v.verifySubject(tt.args.claims, tt.args.cmp, tt.args.required)\n\t\t\tif (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.verifySubject() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_verifyIssuedAt(t *testing.T) {\n\ttype fields struct {\n\t\tleeway    time.Duration\n\t\ttimeFunc  func() time.Time\n\t\tverifyIat bool\n\t}\n\ttype args struct {\n\t\tclaims   Claims\n\t\tcmp      time.Time\n\t\trequired bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"good claim without iat\",\n\t\t\tfields:  fields{verifyIat: true},\n\t\t\targs:    args{claims: MapClaims{}, required: false},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"good claim with iat\",\n\t\t\tfields: fields{verifyIat: true},\n\t\t\targs: args{\n\t\t\t\tclaims:   RegisteredClaims{IssuedAt: NewNumericDate(time.Now())},\n\t\t\t\tcmp:      time.Now().Add(10 * time.Minute),\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\twantErr: nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\tleeway:    tt.fields.leeway,\n\t\t\t\ttimeFunc:  tt.fields.timeFunc,\n\t\t\t\tverifyIat: tt.fields.verifyIat,\n\t\t\t}\n\t\t\tif err := v.verifyIssuedAt(tt.args.claims, tt.args.cmp, tt.args.required); (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.verifyIssuedAt() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_requireNotBefore(t *testing.T) {\n\ttype fields struct {\n\t\tleeway     time.Duration\n\t\ttimeFunc   func() time.Time\n\t\trequireNbf bool\n\t}\n\ttype args struct {\n\t\tclaims   Claims\n\t\tcmp      time.Time\n\t\trequired bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"good claim without nbf\",\n\t\t\tfields:  fields{requireNbf: false},\n\t\t\targs:    args{claims: MapClaims{}, required: false},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"good claim with nbf\",\n\t\t\tfields: fields{requireNbf: true},\n\t\t\targs: args{\n\t\t\t\tclaims:   RegisteredClaims{NotBefore: NewNumericDate(time.Now().Add(time.Minute * -10))},\n\t\t\t\tcmp:      time.Now().Add(10 * time.Minute),\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"token nbf time is in future\",\n\t\t\tfields: fields{requireNbf: true, timeFunc: time.Now},\n\t\t\targs: args{\n\t\t\t\tclaims:   RegisteredClaims{NotBefore: NewNumericDate(time.Now().Add(time.Minute * +10))},\n\t\t\t\tcmp:      time.Now().Add(10 * time.Minute),\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\twantErr: ErrTokenNotValidYet,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\topts := []ParserOption{\n\t\t\t\tWithLeeway(tt.fields.leeway),\n\t\t\t}\n\n\t\t\tif tt.fields.requireNbf {\n\t\t\t\topts = append(opts, WithNotBeforeRequired())\n\t\t\t}\n\n\t\t\tif tt.fields.timeFunc != nil {\n\t\t\t\topts = append(opts, WithTimeFunc(tt.fields.timeFunc))\n\t\t\t}\n\n\t\t\tv := NewValidator(opts...)\n\n\t\t\tif err := v.verifyNotBefore(tt.args.claims, tt.args.cmp, tt.args.required); (err != nil) && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.requireNotBefore() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Validator_verifyAudience(t *testing.T) {\n\ttype fields struct {\n\t\texpectedAud []string\n\t}\n\ttype args struct {\n\t\tclaims       Claims\n\t\tcmp          []string\n\t\texpectAllAud bool\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:   \"fail without audience when expecting one aud match\",\n\t\t\tfields: fields{expectedAud: []string{\"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       MapClaims{},\n\t\t\t\tcmp:          []string{\"example.com\"},\n\t\t\t\texpectAllAud: false,\n\t\t\t},\n\t\t\twantErr: ErrTokenRequiredClaimMissing,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail without audience when expecting all aud matches\",\n\t\t\tfields: fields{expectedAud: []string{\"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       MapClaims{},\n\t\t\t\tcmp:          []string{\"example.com\"},\n\t\t\t\texpectAllAud: true,\n\t\t\t},\n\t\t\twantErr: ErrTokenRequiredClaimMissing,\n\t\t},\n\t\t{\n\t\t\tname:   \"good when audience matches\",\n\t\t\tfields: fields{expectedAud: []string{\"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       RegisteredClaims{Audience: ClaimStrings{\"example.com\"}},\n\t\t\t\tcmp:          []string{\"example.com\"},\n\t\t\t\texpectAllAud: false,\n\t\t\t},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail when audience matches with one value\",\n\t\t\tfields: fields{expectedAud: []string{\"example.org\", \"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       RegisteredClaims{Audience: ClaimStrings{\"example.com\"}},\n\t\t\t\tcmp:          []string{\"example.org\", \"example.com\"},\n\t\t\t\texpectAllAud: false,\n\t\t\t},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail when audience matches with all values\",\n\t\t\tfields: fields{expectedAud: []string{\"example.org\", \"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       RegisteredClaims{Audience: ClaimStrings{\"example.org\", \"example.com\"}},\n\t\t\t\tcmp:          []string{\"example.org\", \"example.com\"},\n\t\t\t\texpectAllAud: true,\n\t\t\t},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail when audience not matching\",\n\t\t\tfields: fields{expectedAud: []string{\"example.org\", \"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       RegisteredClaims{Audience: ClaimStrings{\"example.net\"}},\n\t\t\t\tcmp:          []string{\"example.org\", \"example.com\"},\n\t\t\t\texpectAllAud: false,\n\t\t\t},\n\t\t\twantErr: ErrTokenInvalidAudience,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail when audience not matching all values\",\n\t\t\tfields: fields{expectedAud: []string{\"example.org\", \"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       RegisteredClaims{Audience: ClaimStrings{\"example.org\", \"example.net\"}},\n\t\t\t\tcmp:          []string{\"example.org\", \"example.com\"},\n\t\t\t\texpectAllAud: true,\n\t\t\t},\n\t\t\twantErr: ErrTokenInvalidAudience,\n\t\t},\n\t\t{\n\t\t\tname:   \"fail when audience missing\",\n\t\t\tfields: fields{expectedAud: []string{\"example.org\", \"example.com\"}},\n\t\t\targs: args{\n\t\t\t\tclaims:       MapClaims{},\n\t\t\t\tcmp:          []string{\"example.org\", \"example.com\"},\n\t\t\t\texpectAllAud: true,\n\t\t\t},\n\t\t\twantErr: ErrTokenRequiredClaimMissing,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := &Validator{\n\t\t\t\texpectedAud:  tt.fields.expectedAud,\n\t\t\t\texpectAllAud: tt.args.expectAllAud,\n\t\t\t}\n\n\t\t\terr := v.verifyAudience(tt.args.claims, tt.args.cmp, tt.args.expectAllAud)\n\t\t\tif tt.wantErr == nil && err != nil {\n\t\t\t\tt.Errorf(\"validator.verifyAudience() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t} else if tt.wantErr != nil && !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(\"validator.verifyAudience() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  }
]