Skip to content

Commit

Permalink
Merge branch 'jpadilla:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
0xSolanaceae authored Oct 7, 2024
2 parents ab3fc16 + defdc16 commit 19f8a3e
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 159 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/enforce-changelog-entry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "Update unreleased section in CHANGELOG"
on:
pull_request:
# By default labeled/unlabeled are not included in the pull_request even so we need to list out what we want
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]

permissions:
contents: read

jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: dangoslen/changelog-enforcer@204e7d3ef26579f4cd0fd759c57032656fdf23c7 # v3.6.1
with:
skipLabels: 'Skip-Changelog,dependencies,tests'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ __pycache__/
# Distribution / packaging
.Python
env/
.venv/
build/
develop-eggs/
dist/
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ Changed

- Use ``Sequence`` for parameter types rather than ``List`` where applicable by @imnotjames in `#970 <https://github.com/jpadilla/pyjwt/pull/970>`__
- Remove algorithm requirement from JWT API, instead relying on JWS API for enforcement, by @luhn in `#975 <https://github.com/jpadilla/pyjwt/pull/975>`__
- Add JWK support to JWT encode by @luhn in `#979 <https://github.com/jpadilla/pyjwt/pull/979>`__
- Encoding and decoding payloads using the `none` algorithm

Before:

```
jwt.encode({"payload":"abc"}, key=None, algorithm=None)
```

After:

```
jwt.encode({"payload":"abc"}, key=None, algorithm='none')
```

Fixed
~~~~~
Expand Down
53 changes: 7 additions & 46 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,14 @@ SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build

# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif

# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext

help:
@echo "Please use \`make <target>' where <target> is one of"
Expand All @@ -30,29 +25,24 @@ help:
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"

clean:
rm -rf $(BUILDDIR)/*
-rm -rf $(BUILDDIR)/*

html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
$(SPHINXBUILD) -n -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

Expand Down Expand Up @@ -87,25 +77,17 @@ qthelp:
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyJWT.qhcp"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/structlog.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyJWT.qhc"

applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/structlog.qhc"

devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/PyJWT"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyJWT"
@echo "# mkdir -p $$HOME/.local/share/devhelp/structlog"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/structlog"
@echo "# devhelp"

epub:
Expand All @@ -126,12 +108,6 @@ latexpdf:
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
Expand Down Expand Up @@ -175,18 +151,3 @@ doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."

xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."

pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
59 changes: 56 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ API Reference
Encode the ``payload`` as JSON Web Token.

:param dict payload: JWT claims, e.g. ``dict(iss=..., aud=..., sub=...)``
:param str key: a key suitable for the chosen algorithm:
:param key: a key suitable for the chosen algorithm:

* for **asymmetric algorithms**: PEM-formatted private key, a multiline string
* for **symmetric algorithms**: plain string, sufficiently long for security

:type key: str or bytes or jwt.PyJWK
:param str algorithm: algorithm to sign the token with, e.g. ``"ES256"``.
If ``headers`` includes ``alg``, it will be preferred to this parameter.
If ``key`` is a :class:`jwt.PyJWK` object, by default the key algorithm will be used.
:param dict headers: additional JWT header fields, e.g. ``dict(kid="my-key-id")``.
:param json.JSONEncoder json_encoder: custom JSON encoder for ``payload`` and ``headers``
:rtype: str
Expand All @@ -25,9 +27,11 @@ API Reference
Verify the ``jwt`` token signature and return the token claims.

:param str jwt: the token to be decoded
:param str key: the key suitable for the allowed algorithm
:param key: the key suitable for the allowed algorithm
:type key: str or bytes or jwt.PyJWK

:param list algorithms: allowed algorithms, e.g. ``["ES256"]``
If ``key`` is a :class:`jwt.PyJWK` object, allowed algorithms will default to the key algorithm.

.. warning::

Expand Down Expand Up @@ -63,12 +67,61 @@ API Reference
if ``verify_exp``, ``verify_iat``, and ``verify_nbf`` respectively
is set to ``True``).

:param Union[str, Iterable] audience: optional, the value for ``verify_aud`` check
:param audience: optional, the value for ``verify_aud`` check
:type audience: Union[str, Iterable]
:param str issuer: optional, the value for ``verify_iss`` check
:param float leeway: a time margin in seconds for the expiration check
:rtype: dict
:returns: the JWT claims

.. class:: PyJWK

A class that represents a `JSON Web Key <https://www.rfc-editor.org/rfc/rfc7517>`_.

.. method:: __init__(self, jwk_data, algorithm=None)

:param dict data: The decoded JWK data.
:param algorithm: The key algorithm. If not specific, the key's ``alg`` will be used.
:type algorithm: str or None

.. staticmethod:: from_json(data, algorithm=None)

:param str data: The JWK data, as a JSON string.
:param algorithm: The key algorithm. If not specific, the key's ``alg`` will be used.
:type algorithm: str or None

:returntype: jwt.PyJWK

Create a :class:`jwt.PyJWK` object from a JSON string.

.. property:: algorithm_name

:type: str

The name of the algorithm used by the key.

.. property:: Algorithm

The ``Algorithm`` class associated with the key.

.. property:: key_type

:type: str or None

The ``kty`` property from the JWK.

.. property:: key_id

:type: str or None

The ``kid`` property from the JWK.

.. property:: public_key_use

:type: str or None

The ``use`` property from the JWK.

.. module:: jwt.api_jwt

.. function:: decode_complete(jwt, key="", algorithms=None, options=None, audience=None, issuer=None, leeway=0)
Expand Down
16 changes: 6 additions & 10 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
import re

import sphinx_rtd_theme


def read(*parts) -> str:
"""
Expand Down Expand Up @@ -37,6 +35,7 @@ def find_version(*file_paths) -> str:
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx_rtd_theme",
]

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -92,19 +91,16 @@ def find_version(*file_paths) -> str:

html_theme = "sphinx_rtd_theme"

html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

html_context = {
"extra_css_files": [
# override wide tables in RTD theme
"_static/theme_overrides.css"
]
}
# These paths are either relative to html_static_path
# or fully qualified paths (eg. https://...)
html_css_files = [
"theme_overrides.css",
]

# Output file base name for HTML help builder.
htmlhelp_basename = "PyJWTdoc"
Expand Down
18 changes: 14 additions & 4 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ Encoding & Decoding Tokens with HS256
>>> import jwt
>>> key = "secret"
>>> encoded = jwt.encode({"some": "payload"}, key, algorithm="HS256")
>>> print(encoded)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
>>> jwt.decode(encoded, key, algorithms="HS256")
{'some': 'payload'}
Expand All @@ -25,8 +23,6 @@ RSA encoding and decoding require the ``cryptography`` module. See :ref:`install
>>> private_key = b"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwhvqCC+37A+UXgcvDl+7nbVjDI3QErdZBkI1VypVBMkKKWHM\nNLMdHk0bIKL+1aDYTRRsCKBy9ZmSSX1pwQlO/3+gRs/MWG27gdRNtf57uLk1+lQI\n6hBDozuyBR0YayQDIx6VsmpBn3Y8LS13p4pTBvirlsdX+jXrbOEaQphn0OdQo0WD\noOwwsPCNCKoIMbUOtUCowvjesFXlWkwG1zeMzlD1aDDS478PDZdckPjT96ICzqe4\nO1Ok6fRGnor2UTmuPy0f1tI0F7Ol5DHAD6pZbkhB70aTBuWDGLDR0iLenzyQecmD\n4aU19r1XC9AHsVbQzxHrP8FveZGlV/nJOBJwFwIDAQABAoIBAFCVFBA39yvJv/dV\nFiTqe1HahnckvFe4w/2EKO65xTfKWiyZzBOotBLrQbLH1/FJ5+H/82WVboQlMATQ\nSsH3olMRYbFj/NpNG8WnJGfEcQpb4Vu93UGGZP3z/1B+Jq/78E15Gf5KfFm91PeQ\nY5crJpLDU0CyGwTls4ms3aD98kNXuxhCGVbje5lCARizNKfm/+2qsnTYfKnAzN+n\nnm0WCjcHmvGYO8kGHWbFWMWvIlkoZ5YubSX2raNeg+YdMJUHz2ej1ocfW0A8/tmL\nwtFoBSuBe1Z2ykhX4t6mRHp0airhyc+MO0bIlW61vU/cPGPos16PoS7/V08S7ZED\nX64rkyECgYEA4iqeJZqny/PjOcYRuVOHBU9nEbsr2VJIf34/I9hta/mRq8hPxOdD\n/7ES/ZTZynTMnOdKht19Fi73Sf28NYE83y5WjGJV/JNj5uq2mLR7t2R0ZV8uK8tU\n4RR6b2bHBbhVLXZ9gqWtu9bWtsxWOkG1bs0iONgD3k5oZCXp+IWuklECgYEA27bA\n7UW+iBeB/2z4x1p/0wY+whBOtIUiZy6YCAOv/HtqppsUJM+W9GeaiMpPHlwDUWxr\n4xr6GbJSHrspkMtkX5bL9e7+9zBguqG5SiQVIzuues9Jio3ZHG1N2aNrr87+wMiB\nxX6Cyi0x1asmsmIBO7MdP/tSNB2ebr8qM6/6mecCgYBA82ZJfFm1+8uEuvo6E9/R\nyZTbBbq5BaVmX9Y4MB50hM6t26/050mi87J1err1Jofgg5fmlVMn/MLtz92uK/hU\nS9V1KYRyLc3h8gQQZLym1UWMG0KCNzmgDiZ/Oa/sV5y2mrG+xF/ZcwBkrNgSkO5O\n7MBoPLkXrcLTCARiZ9nTkQKBgQCsaBGnnkzOObQWnIny1L7s9j+UxHseCEJguR0v\nXMVh1+5uYc5CvGp1yj5nDGldJ1KrN+rIwMh0FYt+9dq99fwDTi8qAqoridi9Wl4t\nIXc8uH5HfBT3FivBtLucBjJgOIuK90ttj8JNp30tbynkXCcfk4NmS23L21oRCQyy\nlmqNDQKBgQDRvzEB26isJBr7/fwS0QbuIlgzEZ9T3ZkrGTFQNfUJZWcUllYI0ptv\ny7ShHOqyvjsC3LPrKGyEjeufaM5J8EFrqwtx6UB/tkGJ2bmd1YwOWFHvfHgHCZLP\n34ZNURCvxRV9ZojS1zmDRBJrSo7+/K0t28hXbiaTOjJA18XAyyWmGg==\n-----END RSA PRIVATE KEY-----\n"
>>> public_key = b"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhvqCC+37A+UXgcvDl+7\nnbVjDI3QErdZBkI1VypVBMkKKWHMNLMdHk0bIKL+1aDYTRRsCKBy9ZmSSX1pwQlO\n/3+gRs/MWG27gdRNtf57uLk1+lQI6hBDozuyBR0YayQDIx6VsmpBn3Y8LS13p4pT\nBvirlsdX+jXrbOEaQphn0OdQo0WDoOwwsPCNCKoIMbUOtUCowvjesFXlWkwG1zeM\nzlD1aDDS478PDZdckPjT96ICzqe4O1Ok6fRGnor2UTmuPy0f1tI0F7Ol5DHAD6pZ\nbkhB70aTBuWDGLDR0iLenzyQecmD4aU19r1XC9AHsVbQzxHrP8FveZGlV/nJOBJw\nFwIDAQAB\n-----END PUBLIC KEY-----\n"
>>> encoded = jwt.encode({"some": "payload"}, private_key, algorithm="RS256")
>>> print(encoded)
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.ACNvAmKejouaO7fOVAqiqTQ2nE7jtvA2hpUJk3XduO9qp1mhCSJGIMQRNokyzNZgJFibGLZhcfwf0_4amlYqwBQ3HTB58zDdV-iAoSIxnvOERMc9qxUKNupAs4B0aL0vzIvpIMoDxIMDAnNviDIDzWPnprnpwHNdza7Y40_O1h4trmJcstE0xjmcyV0CEemwOCSARcFnxVK8rE5dItdL05IWrzOx-twBPH5jIU9zwtDvyci9LXUpG010CiejDxI1Iu0ezbN8iX2wkNVbmsYPNnMvgmGCMJbSiatzXJNDdrGXM_YiJKxXvOHcL6aUDYM0vc5oymT4-aAHwKEUB8592A
>>> jwt.decode(encoded, public_key, algorithms=["RS256"])
{'some': 'payload'}
Expand All @@ -49,6 +45,20 @@ If you are repeatedly encoding with the same private key, reusing the same
``RSAPrivateKey`` also has performance benefits because it avoids the
CPU-intensive ``RSA_check_key`` primality test.

Encoding & Decoding Tokens with PS256 (RSA)
-------------------------------------------

RSA encoding and decoding require the ``cryptography`` module. See :ref:`installation_cryptography`.

.. code-block:: pycon
>>> import jwt
>>> private_key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAuNhCS6bodtd+PvKqNj+tYZYqTNMDkf0rcptgHhecSsMP9Vay\n+6NvJk1tC+IajPaE4yRJVY4jFqEt3A0MJ9sKe5mWDYFmzW/L6VzQvQ+0nrMc1YTE\nDpOf7BQhlW5W0mDj5SwSR50Lxg/acb+SMWq6zmhuAoLRapH17K2RWONA2vr2frox\nJ6N9TGtrQHygDb0p9D6jPnXEe4y+zBuj6o0bCkJgCVNM+CU19xBepj5caetYV28/\n49yl5XPi93n1ATU+7aGAKxuvjudODuHhF/UsZScMFSHeZW367eQldTB2w9uoIIzW\nO46tKimr21zYifMimjwnBQ/PLDqc7HqY0Y/rLQIDAQABAoIBAAdu0CD7/Iu61/LE\nDfV8fgZXOYA5WVgSLCBsVbh1Y+2FsStBFJVrLwRanLCbo6GuJWMqNGC3ryWGebJI\nPAg7lfepEhBHodClAY1yvq9mOvHJa2Fn+KegEWWMMbAxQwCBW5NS6waXhBUE0i3n\ncYOB3TKA9IYuqH52kW22VQqT/imlWEb28pJJT49YfggmOOtAkrKerokO53lAfrJA\ntm8lYvxXnfnuYh7zI835RpZJ1PeaYrMqyAwT+StD9hPKGWGpN1gCJijjcK0aapvq\nMLET/JxMxxcLsINOeLtGhMKawmET3J/esJTumOE2L77MFG83rlCPbsSfLdSAI2WD\nSe3Q2ikCgYEA7JzmVrPh7G/oILLzIfk8GHFACRTtlE5SDEpFq+ARMprfcBXpkl+Q\naWqQ3vuSH7oiAQKlvo3We6XXohCMMDU2DyMaXiQMk73R83fMwbFnFcqFhbzx2zpm\nj/neHIViEi/N69SHPxl+vnUTfeVZptibNGS+ch3Ubawt3wCaWr+IdAcCgYEAx/19\ns5ryq2oTQCD5GfIqW73LAUly5RqENLvKHZ2z+mZ0pp7dc5449aDsHPLXLl1YC3mO\nlZZk+8Jh5yrpHyljiIYwh/1y0WsbungMlH6lG9JigcN8R2Tk9hWT7DQL0fm0dYoQ\njkwr/gJv6PW0piLsR0vsQQpm/F/ucZolVPQIoisCgYA5XXzWznvax/LeYqRhuzxf\nrK1axlEnYKmxwxwLJKLmwvejBB0B2Nt5Q1XmSdXOjWELH6oxfc/fYIDcEOj8ExqN\nJvSQmGrYMvBA9+2TlEAq31Pp7boxbYJKK8k23vu87wwcvgUgPj0lTdsw7bcDpYZT\neI1Xu3WyNUlVxJ6nm8IoZwKBgG6YPjVekKg+htrF4Tt58fa95E+X4JPVsBrBZqou\nFeN5WTTzUZ+odfNPxILVwC2BrTjbRgBvJPUcr6t4zWZQKxzKqHfrrt0kkDb0QHC2\nAHR8ScFc65NHtl5n3F+ZAJhjsGn3qeQnN4TGsEBx8C6XzXY4BDSLnhweqOvlxJNQ\nSJ31AoGAX/UN5xR6PlCgPw5HWfGd7+4sArkjA36DAXvrAgW/6/mxZZzoGA1swYdZ\nq2uGp38UEKkxKTrhR4J6eR5DsLAfl/KQBbNC42vqZwe9YrS4hNQFR14GwlyJhdLx\nKQD/JzHwNQN5+o+hy0lJavTw9NwAAb1ZzTgvq6fPwEG0b9hn0SI=\n-----END RSA PRIVATE KEY-----\n"
>>> public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNhCS6bodtd+PvKqNj+t\nYZYqTNMDkf0rcptgHhecSsMP9Vay+6NvJk1tC+IajPaE4yRJVY4jFqEt3A0MJ9sK\ne5mWDYFmzW/L6VzQvQ+0nrMc1YTEDpOf7BQhlW5W0mDj5SwSR50Lxg/acb+SMWq6\nzmhuAoLRapH17K2RWONA2vr2froxJ6N9TGtrQHygDb0p9D6jPnXEe4y+zBuj6o0b\nCkJgCVNM+CU19xBepj5caetYV28/49yl5XPi93n1ATU+7aGAKxuvjudODuHhF/Us\nZScMFSHeZW367eQldTB2w9uoIIzWO46tKimr21zYifMimjwnBQ/PLDqc7HqY0Y/r\nLQIDAQAB\n-----END PUBLIC KEY-----\n"
>>> encoded = jwt.encode({"some": "payload"}, private_key, algorithm="PS256")
>>> jwt.decode(encoded, public_key, algorithms=["PS256"])
{'some': 'payload'}
Specifying Additional Headers
-----------------------------

Expand Down
14 changes: 11 additions & 3 deletions jwt/api_jws.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,21 @@ def get_algorithm_by_name(self, alg_name: str) -> Algorithm:
def encode(
self,
payload: bytes,
key: AllowedPrivateKeys | str | bytes,
algorithm: str | None = "HS256",
key: AllowedPrivateKeys | PyJWK | str | bytes,
algorithm: str | None = None,
headers: dict[str, Any] | None = None,
json_encoder: type[json.JSONEncoder] | None = None,
is_payload_detached: bool = False,
sort_headers: bool = True,
) -> str:
# declare a new var to narrow the type for type checkers
algorithm_: str = algorithm if algorithm is not None else "none"
if algorithm is None:
if isinstance(key, PyJWK):
algorithm_ = key.algorithm_name
else:
algorithm_ = "HS256"
else:
algorithm_ = algorithm

# Prefer headers values if present to function parameters.
if headers:
Expand Down Expand Up @@ -152,6 +158,8 @@ def encode(
signing_input = b".".join(segments)

alg_obj = self.get_algorithm_by_name(algorithm_)
if isinstance(key, PyJWK):
key = key.key
key = alg_obj.prepare_key(key)
signature = alg_obj.sign(signing_input, key)

Expand Down
4 changes: 2 additions & 2 deletions jwt/api_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def _get_default_options() -> dict[str, bool | list[str]]:
def encode(
self,
payload: dict[str, Any],
key: AllowedPrivateKeys | str | bytes,
algorithm: str | None = "HS256",
key: AllowedPrivateKeys | PyJWK | str | bytes,
algorithm: str | None = None,
headers: dict[str, Any] | None = None,
json_encoder: type[json.JSONEncoder] | None = None,
sort_headers: bool = True,
Expand Down
Loading

0 comments on commit 19f8a3e

Please sign in to comment.