Skip to content

Commit

Permalink
[16.0] Use PyJWT instead of python-jose
Browse files Browse the repository at this point in the history
  • Loading branch information
Robrecht committed May 23, 2024
1 parent b7c454d commit f920957
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 36 deletions.
4 changes: 2 additions & 2 deletions auth_oidc/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

{
"name": "Authentication OpenID Connect",
"version": "16.0.1.2.0",
"version": "16.0.1.3.0",
"license": "AGPL-3",
"author": (
"ICTSTUDIO, André Schenkels, "
Expand All @@ -14,7 +14,7 @@
"maintainers": ["sbidoul"],
"website": "https://github.com/OCA/server-auth",
"summary": "Allow users to login through OpenID Connect Provider",
"external_dependencies": {"python": ["python-jose"]},
"external_dependencies": {"python": ["pyjwt"]},
"depends": ["auth_oauth"],
"data": ["views/auth_oauth_provider.xml", "data/auth_oauth_data.xml"],
"demo": ["demo/local_keycloak.xml"],
Expand Down
35 changes: 11 additions & 24 deletions auth_oidc/models/auth_oauth_provider.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
# Copyright 2016 ICTSTUDIO <http://www.ictstudio.eu>
# Copyright 2021 ACSONE SA/NV <https://acsone.eu>
# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

import logging
import secrets

import requests
import jwt
from jwt.exceptions import PyJWTError

from odoo import fields, models, tools

try:
from jose import jwt
from jose.exceptions import JWSError, JWTError
except ImportError:
logging.getLogger(__name__).debug("jose library not installed")


class AuthOauthProvider(models.Model):
_inherit = "auth.oauth.provider"
Expand Down Expand Up @@ -51,18 +44,12 @@ class AuthOauthProvider(models.Model):
)

@tools.ormcache("self.jwks_uri", "kid")
def _get_keys(self, kid):
r = requests.get(self.jwks_uri, timeout=10)
r.raise_for_status()
response = r.json()
# the keys returned here should follow
def _get_keys(self, kid, access_token=None):
# JWS Notes on Key Selection
# https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-signature#appendix-D
return [
key
for key in response["keys"]
if kid is None or key.get("kid", None) == kid
]
jwks_client = jwt.PyJWKClient(self.jwks_uri, cache_jwk_set=True, lifespan=360)
signing_key = jwks_client.get_signing_key_from_jwt(access_token)
return signing_key

def _map_token_values(self, res):
if self.token_map:
Expand All @@ -81,28 +68,28 @@ def _parse_id_token(self, id_token, access_token):
return res

def _decode_id_token(self, access_token, id_token, kid):
keys = self._get_keys(kid)
keys = self._get_keys(access_token)
if len(keys) > 1 and kid is None:
# https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1
# If there are multiple keys in the referenced JWK Set document, a kid
# value MUST be provided in the JOSE Header.
raise JWTError(
raise PyJWTError(
"OpenID Connect requires kid to be set if there is more"
" than one key in the JWKS"
)

error = None
# we accept multiple keys with the same kid in case a key gets rotated.
for key in keys:
try:
values = jwt.decode(
id_token,
access_token,
key,
algorithms=["RS256"],
audience=self.client_id,
access_token=access_token,
)
return values
except (JWTError, JWSError) as e:
except PyJWTError as e:
error = e
if error:
raise error
Expand Down
11 changes: 7 additions & 4 deletions auth_oidc/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.

See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Expand Down Expand Up @@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }

pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
Expand All @@ -300,7 +301,7 @@
span.pre {
white-space: pre }

span.problematic {
span.problematic, pre.problematic {
color: red }

span.section-subtitle {
Expand Down Expand Up @@ -579,7 +580,9 @@ <h2><a class="toc-backref" href="#toc-entry-19">Contributors</a></h2>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-20">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
Expand Down
32 changes: 27 additions & 5 deletions auth_oidc/tests/test_auth_oidc_auth_code.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Copyright 2021 ACSONE SA/NV <https://acsone.eu>
# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

import base64
import contextlib
import json
from urllib.parse import parse_qs, urlparse

import jwt
import responses
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from jose import jwt
from jose.exceptions import JWTError
from jose.utils import long_to_base64
from jwt.exceptions import PyJWTError

import odoo
from odoo.exceptions import AccessDenied
Expand All @@ -22,6 +22,28 @@

BASE_URL = "http://localhost:%s" % odoo.tools.config["http_port"]

try:
from cryptography.utils import int_to_bytes as _long_to_bytes

def long_to_bytes(n, blocksize=0):
return _long_to_bytes(n, blocksize or None)

except ImportError:
from ecdsa.ecdsa import int_to_string as _long_to_bytes

def long_to_bytes(n, blocksize=0):
ret = _long_to_bytes(n)
if blocksize == 0:
return ret
else:
assert len(ret) <= blocksize
padding = blocksize - len(ret)
return b"\x00" * padding + ret


def long_to_base64(data, size=0):
return base64.urlsafe_b64encode(long_to_bytes(data, size)).strip(b"=")


@contextlib.contextmanager
def MockRequest(env):
Expand Down Expand Up @@ -211,7 +233,7 @@ def test_login_without_kid_multiple_keys_in_jwks(self):
)

with self.assertRaises(
JWTError,
PyJWTError,
msg="OpenID Connect requires kid to be set if there is"
" more than one key in the JWKS",
):
Expand All @@ -232,7 +254,7 @@ def test_login_without_matching_key(self):
keys=[{"kid": "other_key_id", "keys": [self.second_key_public_pem]}],
)

with self.assertRaises(JWTError):
with self.assertRaises(PyJWTError):
with MockRequest(self.env):
self.env["res.users"].auth_oauth(
self.provider_rec.id,
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ cryptography
email_validator
pyjwt
pysaml2
python-jose
python-ldap

0 comments on commit f920957

Please sign in to comment.