Skip to content
Open
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
7 changes: 2 additions & 5 deletions auth_jwt/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

========
Auth JWT
========
Expand All @@ -17,7 +13,7 @@ Auth JWT
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github
Expand Down Expand Up @@ -141,6 +137,7 @@ Contributors

- Stéphane Bidoul <stephane.bidoul@acsone.eu>
- Mohamed Alkobrosli <malkobrosly@kencove.com>
- Don Kendall <kendall@donkendall.com>

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion auth_jwt/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"name": "Auth JWT",
"summary": """
JWT bearer token authentication.""",
"version": "18.0.1.0.0",
"version": "18.0.1.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version should not be modified. It will be automatically bumped at merge.

"license": "LGPL-3",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"maintainers": ["sbidoul"],
Expand Down
36 changes: 31 additions & 5 deletions auth_jwt/models/auth_jwt_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,19 @@ class AuthJwtValidator(models.Model):
],
default="RS256",
)
audience_type = fields.Selection(
[
("aud", "Audience"),
("group", "Group"),
("scope", "Scope"),
("custom", "Custom"),
],
required=True,
default="aud",
)
audience_type_custom = fields.Char(required=False, help="payload key to validate")
audience = fields.Char(
required=True, help="Comma separated list of audiences, to validate aud."
required=True, help="Comma separated list of attribute needed."
)
issuer = fields.Char(required=True, help="To validate iss.")
user_id_strategy = fields.Selection(
Expand Down Expand Up @@ -160,7 +171,7 @@ def _get_validator_by_name(self, validator_name):

@tools.ormcache("self.public_key_jwk_uri", "kid")
def _get_key(self, kid):
jwks_client = PyJWKClient(self.public_key_jwk_uri, cache_keys=False)
jwks_client = PyJWKClient(self.public_key_jwk_uri)
return jwks_client.get_signing_key(kid).key

def _encode(self, payload, secret, expire):
Expand Down Expand Up @@ -194,20 +205,35 @@ def _decode(self, token, secret=None):
raise UnauthorizedInvalidToken() from e
key = self._get_key(header.get("kid"))
algorithm = self.public_key_algorithm
aud = (self.audience or "").split(",") if self.audience_type == "aud" else None
try:
payload = jwt.decode(
token,
key=key,
algorithms=[algorithm],
options=dict(
require=["exp", "aud", "iss"],
require=["exp", "iss"],
verify_exp=True,
verify_aud=True,
verify_iss=True,
),
audience=self.audience.split(","),
audience=aud,
issuer=self.issuer,
)
payload_key = (
self.audience_type_custom
if self.audience_type == "custom"
else self.audience_type
)
if len((self.audience or "").split(",") or []) > 0:
for key_value in (self.audience or "").split(","):
payload_value = (
payload.get(payload_key)
if isinstance(payload.get(payload_key), list)
else (payload.get(payload_key) or "").split(" ")
)
if key_value in payload_value:
return payload
raise UnauthorizedInvalidToken()
except Exception as e:
_logger.info("Invalid token: %s", e)
raise UnauthorizedInvalidToken() from e
Expand Down
1 change: 1 addition & 0 deletions auth_jwt/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Stéphane Bidoul \<<stephane.bidoul@acsone.eu>\>
- Mohamed Alkobrosli \<<malkobrosly@kencove.com>\>
- Don Kendall \<<kendall@donkendall.com>\>
29 changes: 12 additions & 17 deletions auth_jwt/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Auth JWT</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="auth-jwt">
<h1 class="title">Auth JWT</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="auth-jwt">
<h1>Auth JWT</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0257cb75b9a02ab9b3f1aeebe8e0c5aee0b983f8b5ac1692132897dfb1986d02
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-auth/tree/18.0/auth_jwt"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-18-0/server-auth-18-0-auth_jwt"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-auth/tree/18.0/auth_jwt"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-18-0/server-auth-18-0-auth_jwt"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>JWT bearer token authentication.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
Expand All @@ -391,11 +386,11 @@ <h1>Auth JWT</h1>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Installation</a></h1>
<p>This module requires the <tt class="docutils literal">pyjwt</tt> library to be installed.</p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>This module lets developpers add a new <tt class="docutils literal">jwt</tt> authentication method on
Odoo controller routes.</p>
<p>To use it, you must:</p>
Expand Down Expand Up @@ -459,30 +454,31 @@ <h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
with a different user by providing a new JWT token.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-auth/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-auth/issues/new?body=module:%20auth_jwt%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li>Stéphane Bidoul &lt;<a class="reference external" href="mailto:stephane.bidoul&#64;acsone.eu">stephane.bidoul&#64;acsone.eu</a>&gt;</li>
<li>Mohamed Alkobrosli &lt;<a class="reference external" href="mailto:malkobrosly&#64;kencove.com">malkobrosly&#64;kencove.com</a>&gt;</li>
<li>Don Kendall &lt;<a class="reference external" href="mailto:kendall&#64;donkendall.com">kendall&#64;donkendall.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-7">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" />
Expand All @@ -497,6 +493,5 @@ <h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
43 changes: 43 additions & 0 deletions auth_jwt/tests/test_auth_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,46 @@ def test_public_or_jwt_valid_token(self):
with self._mock_request(authorization=authorization) as request:
self.env["ir.http"]._auth_method_public_or_jwt_validator()
assert request.jwt_payload["aud"] == "me"

def test_valid_token_with_audience_type_aud(self):
validator = self._create_validator("val1", audience="client1,client2")
validator.audience_type = "aud"
token = self._create_token(audience="client1")
payload = validator._decode(token)
self.assertEqual(payload["aud"], "client1")

def test_invalid_audience_with_audience_type_aud(self):
validator = self._create_validator("val2", audience="client1,client2")
validator.audience_type = "aud"
token = self._create_token(audience="otherclient")
with self.assertRaises(UnauthorizedInvalidToken):
validator._decode(token)

def test_valid_token_with_custom_audience_type(self):
validator = self._create_validator("val3", audience="read,write")
validator.audience_type = "custom"
validator.audience_type_custom = "scope"
payload = {
"iss": "http://the.issuer",
"exp": time.time() + 100,
"scope": "read write", # token claim space-separated
}
token = jwt.encode(payload, "thesecret", algorithm="HS256")
decoded = validator._decode(token)
self.assertIn("read", decoded["scope"].split(" "))

def test_invalid_custom_audience_type(self):
validator = self._create_validator("val4", audience="read")
validator.audience_type = "custom"
validator.audience_type_custom = "scope"
token = self._create_token()
# No scope claim in token
with self.assertRaises(UnauthorizedInvalidToken):
validator._decode(token)

def test_invalid_signature_rejected(self):
validator = self._create_validator("val5", audience="client1")
validator.audience_type = "aud"
token = self._create_token(audience="client1", key="wrongsecret")
with self.assertRaises(UnauthorizedInvalidToken):
validator._decode(token)
6 changes: 5 additions & 1 deletion auth_jwt/views/auth_jwt_validator_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
<field name="next_validator_id" />
</group>
<group colspan="2" string="Token validation">
<field name="audience_type" />
<field
name="audience_type_custom"
invisible="audience_type != 'custom'"
/>
<field name="audience" />
<field name="issuer" />
<field name="signature_type" />
<field
name="secret_key"
Expand Down