Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- name: Check out code
uses: actions/checkout@v3
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ CHANGELOG

This document describes changes between each past release.

0.9.0 (2025-12-11)
==================

- Migrate to use jwtoxide
- Remove Python 3.8 support

0.8.1 (2025-05-01)
==================

Expand Down
2 changes: 1 addition & 1 deletion fxa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

"""

__version__ = "0.8.1"
__version__ = "0.9.0"
__ver_tuple__ = tuple(__version__.split("."))


Expand Down
24 changes: 20 additions & 4 deletions fxa/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from urllib.parse import urlparse, urlunparse, urlencode, parse_qs

import jwt
from jwtoxide import DecodingKey, Jwk, ValidationOptions, decode
from fxa.cache import MemoryCache, DEFAULT_CACHE_EXPIRY
from fxa.constants import PRODUCTION_URLS
from fxa.errors import OutOfProtocolError, ScopeMismatchError, TrustError
Expand Down Expand Up @@ -198,15 +199,30 @@ def authorize_token(self, session, scope=None, client_id=None):
return resp['access_token']

def _verify_jwt_token(self, key, token):
pubkey = jwt.algorithms.RSAAlgorithm.from_jwk(key)
# The FxA OAuth ecosystem currently doesn't make good use of aud, and
# instead relies on scope for restricting which services can accept
# which tokens. So there's no value in checking it here, and in fact if
# we check it here, it fails because the right audience isn't being
# requested.
decoded = jwt.decode(
token, pubkey, algorithms=['RS256'], options={'verify_aud': False}
)
try:
# Try to first decode with jwtoxide
decoded = decode(
token,
DecodingKey.from_jwk(Jwk.from_json(key)),
ValidationOptions(
aud=None,
iss=None,
required_spec_claims={"iat", "exp"},
validate_aud=False,
algorithms=["RS256"],
),
)
except Exception:
# If something goes wrong, fallback to PyJWT
pubkey = jwt.algorithms.RSAAlgorithm.from_jwk(key)
Comment thread
noahpodgurski marked this conversation as resolved.
decoded = jwt.decode(
token, pubkey, algorithms=["RS256"], options={"verify_aud": False}
)
# Ref https://tools.ietf.org/html/rfc7515#section-4.1.9 the `typ` header
# is lowercase and has an implicit default `application/` prefix.
typ = jwt.get_unverified_header(token).get('typ', '')
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ license = "MPL-2.0"
authors = [
{ name = "Mozilla Services", email = "services-dev@mozilla.org" },
]
requires-python = ">=3.8"
requires-python = ">=3.9"
classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -32,6 +31,7 @@ dynamic = [ "version" ]
dependencies = [
"cryptography",
"hawkauthlib",
"jwtoxide==0.2.0",
"pyjwt",
"requests>=2.4.2",
]
Expand Down Expand Up @@ -71,7 +71,7 @@ cov = "pytest --cov-config=pyproject.toml --cov=fxa/ --cov-report term-missing {

[[tool.hatch.envs.test.matrix]]
# Note: When changing these, also update the .github/workflows/test.yml file.
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
python = ["3.9", "3.10", "3.11", "3.12"]

[tool.flake8]
max-line-length = 99
Loading