moved from josepy to pyjwt

This commit is contained in:
Oscar Krause 2025-05-15 09:24:35 +02:00
parent 26a5bdb320
commit f58e54cf28
8 changed files with 28 additions and 30 deletions

View File

@ -1,7 +1,7 @@
# https://packages.debian.org/hu/ # https://packages.debian.org/hu/
fastapi==0.92.0 fastapi==0.92.0
uvicorn[standard]==0.17.6 uvicorn[standard]==0.17.6
josepy==2.0.0 pyjwt==2.10.1
cryptography==38.0.4 cryptography==38.0.4
python-dateutil==2.8.2 python-dateutil==2.8.2
sqlalchemy==1.4.46 sqlalchemy==1.4.46

View File

@ -1,7 +1,7 @@
# https://packages.ubuntu.com # https://packages.ubuntu.com
fastapi==0.101.0 fastapi==0.101.0
uvicorn[standard]==0.27.1 uvicorn[standard]==0.27.1
josepy==2.0.0 pyjwt==2.10.1
cryptography==41.0.7 cryptography==41.0.7
python-dateutil==2.8.2 python-dateutil==2.8.2
sqlalchemy==1.4.50 sqlalchemy==1.4.50

View File

@ -1,7 +1,7 @@
# https://packages.ubuntu.com # https://packages.ubuntu.com
fastapi==0.110.3 fastapi==0.110.3
uvicorn[standard]==0.30.3 uvicorn[standard]==0.30.3
josepy==2.0.0 pyjwt==2.10.1
cryptography==42.0.5 cryptography==42.0.5
python-dateutil==2.9.0 python-dateutil==2.9.0
sqlalchemy==2.0.32 sqlalchemy==2.0.32

View File

@ -8,7 +8,7 @@ pkgdesc='NVIDIA DLS server implementation with FastAPI'
arch=('any') arch=('any')
url='https://git.collinwebdesigns.de/oscar.krause/fastapi-dls' url='https://git.collinwebdesigns.de/oscar.krause/fastapi-dls'
license=('MIT') license=('MIT')
depends=('python' 'python-josepy' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python-cryptography' 'uvicorn' 'python-markdown' 'openssl') depends=('python' 'python-jwt' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python-cryptography' 'uvicorn' 'python-markdown' 'openssl')
provider=("$pkgname") provider=("$pkgname")
install="$pkgname.install" install="$pkgname.install"
backup=('etc/default/fastapi-dls') backup=('etc/default/fastapi-dls')

View File

@ -172,12 +172,12 @@ test:apt:
parallel: parallel:
matrix: matrix:
- IMAGE: - IMAGE:
- debian:trixie-slim # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" - debian:trixie-slim # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" or "python3-jwt"
- debian:bookworm-slim # EOL: June 06, 2026 - debian:bookworm-slim # EOL: June 06, 2026
- debian:bullseye-slim # EOL: June 06, 2026 - debian:bullseye-slim # EOL: June 06, 2026
- ubuntu:24.04 # EOL: April 2036 - ubuntu:24.04 # EOL: April 2036
- ubuntu:24.10 # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" - ubuntu:24.10 # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" or "python3-jwt"
- ubuntu:25.04 # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" - ubuntu:25.04 # EOL: t.b.a.; "python3-jose" not available, but "python3-josepy" or "python3-jwt"
needs: needs:
- job: build:apt - job: build:apt
artifacts: true artifacts: true

View File

@ -15,7 +15,7 @@ from dotenv import load_dotenv
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.requests import Request from fastapi.requests import Request
from fastapi.responses import Response, RedirectResponse, StreamingResponse from fastapi.responses import Response, RedirectResponse, StreamingResponse
from josepy import jws, jwk, RS256 import jwt
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from starlette.middleware.cors import CORSMiddleware from starlette.middleware.cors import CORSMiddleware
@ -62,8 +62,8 @@ my_si_certificate = Cert.from_file(ca_setup.si_certificate_filename)
my_si_private_key = PrivateKey.from_file(ca_setup.si_private_key_filename) my_si_private_key = PrivateKey.from_file(ca_setup.si_private_key_filename)
my_si_public_key = my_si_private_key.public_key() my_si_public_key = my_si_private_key.public_key()
jwt_encode_key = jwk.JWK.load(my_si_private_key.pem()) jwt_encode_key = my_si_private_key.pem() # todo: replace directly in code
jwt_decode_key = jwk.JWK.load(my_si_private_key.public_key().pem()) jwt_decode_key = my_si_private_key.public_key().pem() # todo: replace directly in code
# Logging # Logging
LOG_LEVEL = logging.DEBUG if DEBUG else logging.INFO LOG_LEVEL = logging.DEBUG if DEBUG else logging.INFO
@ -115,9 +115,7 @@ def __get_token(request: Request) -> dict:
token = authorization_header.split(' ')[1] token = authorization_header.split(' ')[1]
# return jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False}) # return jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
_ = jws.Signature() return jwt.decode(jwt=token, key=jwt_decode_key, algorithms=['RS256'], options={'verify_aud': False})
_.verify(payload=token.encode('utf-8'), key=jwt_decode_key)
return _.to_partial_json()
# Endpoints # Endpoints
@ -299,8 +297,7 @@ async def _client_token():
} }
# content = jws.sign(payload, key=jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256) # content = jws.sign(payload, key=jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256)
payload = json_dumps(payload).encode('utf-8') content = jwt.encode(payload=payload, key=jwt_encode_key, headers=None, algorithm='RS256')
content = jws.Signature.sign(payload=payload, key=jwt_encode_key, alg=RS256, include_jwk=False)
# response = StreamingResponse(iter([content]), media_type="text/plain") # response = StreamingResponse(iter([content]), media_type="text/plain")
response = StreamingResponse(iter(content), media_type="text/plain") response = StreamingResponse(iter(content), media_type="text/plain")
@ -393,8 +390,7 @@ async def auth_v1_code(request: Request):
} }
# auth_code = jws.sign(payload, key=jwt_encode_key, headers={'kid': payload.get('kid')}, algorithm=ALGORITHMS.RS256) # auth_code = jws.sign(payload, key=jwt_encode_key, headers={'kid': payload.get('kid')}, algorithm=ALGORITHMS.RS256)
payload = json_dumps(payload).encode('utf-8') auth_code = jwt.encode(payload=payload, key=jwt_encode_key, headers={'kid': payload.get('kid')}, algorithm='RS256')
auth_code = jws.Signature.sign(payload=payload, key=jwt_encode_key, alg=RS256, include_jwk=True)
response = { response = {
"auth_code": auth_code, "auth_code": auth_code,
@ -412,7 +408,8 @@ async def auth_v1_token(request: Request):
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC) j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
try: try:
payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key, algorithms=ALGORITHMS.RS256) #payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key, algorithms=ALGORITHMS.RS256)
payload = jwt.decode(jwt=j.get('auth_code'), key=jwt_decode_key, algorithms=['RS256'])
except JWTError as e: except JWTError as e:
response = {'status': 400, 'title': 'invalid token', 'detail': str(e)} response = {'status': 400, 'title': 'invalid token', 'detail': str(e)}
return Response(content=json_dumps(response), media_type='application/json', status_code=400) return Response(content=json_dumps(response), media_type='application/json', status_code=400)
@ -478,8 +475,9 @@ async def leasing_v1_config_token(request: Request):
}, },
} }
my_jwt_encode_key = jwk.construct(my_si_private_key.pem().decode('utf-8'), algorithm=ALGORITHMS.RS256) # my_jwt_encode_key = jwk.construct(my_si_private_key.pem().decode('utf-8'), algorithm=ALGORITHMS.RS256)
config_token = jws.sign(payload, key=my_jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256) # config_token = jws.sign(payload, key=my_jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256)
config_token = jwt.encode(payload=payload, key=my_si_private_key.pem(), headers=None, algorithm='RS256')
response_ca_chain = my_ca_certificate.pem().decode('utf-8').strip() response_ca_chain = my_ca_certificate.pem().decode('utf-8').strip()

View File

@ -1,6 +1,6 @@
fastapi==0.115.12 fastapi==0.115.12
uvicorn[standard]==0.34.2 uvicorn[standard]==0.34.2
josepy==2.0.0 pyjwt==2.10.1
cryptography==44.0.3 cryptography==44.0.3
python-dateutil==2.9.0 python-dateutil==2.9.0
sqlalchemy==2.0.41 sqlalchemy==2.0.41

View File

@ -4,13 +4,13 @@ from base64 import b64encode as b64enc
from calendar import timegm from calendar import timegm
from datetime import datetime, UTC from datetime import datetime, UTC
from hashlib import sha256 from hashlib import sha256
from json import loads as json_loads, dumps as json_dumps
from uuid import uuid4, UUID from uuid import uuid4, UUID
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.hashes import SHA256 from cryptography.hazmat.primitives.hashes import SHA256
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from jose import jwt, jwk, jws import jwt
from jose.constants import ALGORITHMS
from starlette.testclient import TestClient from starlette.testclient import TestClient
# add relative path to use packages as they were in the app/ dir # add relative path to use packages as they were in the app/ dir
@ -38,12 +38,12 @@ my_si_public_key = my_si_private_key.public_key()
my_si_public_key_as_pem = my_si_private_key.public_key().pem() my_si_public_key_as_pem = my_si_private_key.public_key().pem()
my_si_certificate = Cert.from_file(ca_setup.si_certificate_filename) my_si_certificate = Cert.from_file(ca_setup.si_certificate_filename)
jwt_encode_key = jwk.construct(my_si_private_key_as_pem, algorithm=ALGORITHMS.RS256) jwt_encode_key = my_si_private_key.pem()
jwt_decode_key = jwk.construct(my_si_public_key_as_pem, algorithm=ALGORITHMS.RS256) jwt_decode_key = my_si_private_key.public_key().pem()
def __bearer_token(origin_ref: str) -> str: def __bearer_token(origin_ref: str) -> str:
token = jwt.encode({"origin_ref": origin_ref}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) # token = jwt.encode({"origin_ref": origin_ref}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256)
token = jwt.encode(payload={"origin_ref": origin_ref}, key=jwt_encode_key, algorithm='RS256')
token = f'Bearer {token}' token = f'Bearer {token}'
return token return token
@ -145,12 +145,12 @@ def test_config_token():
assert nv_si_certificate.public_key().mod() == nv_response_public_key.get('mod')[0] assert nv_si_certificate.public_key().mod() == nv_response_public_key.get('mod')[0]
assert nv_si_certificate.authority_key_identifier() == nv_ca_chain.subject_key_identifier() assert nv_si_certificate.authority_key_identifier() == nv_ca_chain.subject_key_identifier()
nv_jwt_decode_key = jwk.construct(nv_response_public_cert, algorithm=ALGORITHMS.RS256) # nv_jwt_decode_key = jwk.construct(nv_response_public_cert, algorithm=ALGORITHMS.RS256)
nv_response_config_token = response.json().get('configToken') nv_response_config_token = response.json().get('configToken')
payload = jws.verify(nv_response_config_token, key=nv_jwt_decode_key, algorithms=ALGORITHMS.RS256) #payload = jws.verify(nv_response_config_token, key=nv_jwt_decode_key, algorithms=ALGORITHMS.RS256)
payload = json.loads(payload) payload = jwt.decode(jwt=nv_response_config_token, key=nv_si_certificate.public_key().pem(), algorithms=['RS256'], options={'verify_signature': False})
assert payload.get('iss') == 'NLS Service Instance' assert payload.get('iss') == 'NLS Service Instance'
assert payload.get('aud') == 'NLS Licensed Client' assert payload.get('aud') == 'NLS Licensed Client'
assert payload.get('service_instance_ref') == INSTANCE_REF assert payload.get('service_instance_ref') == INSTANCE_REF