Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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 webservice/tests/test_oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def test_oauth2_flow_compute_with_ui(self):
form_xmlid = "webservice.webservice_backend_form_view"
for auth_type, oauth2_flow in [
(tp, fl)
for tp in ws._fields["auth_type"].get_values(ws.env)
for tp in ["none", "user_pwd", "api_key", "oauth2"]
for fl in ws._fields["oauth2_flow"].get_values(ws.env)
]:
next_ws_id = ws.sudo().search([], order="id desc", limit=1).id + 1
Expand Down
110 changes: 110 additions & 0 deletions webservice_client_certificate_auth/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
==============================================
WebService - Client Certificate Authentication
==============================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:4a2f96ce5a7f3c69babb934fd5a9e2bb530fd01e335c3526d5f499774b59fd36
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb--api-lightgray.png?logo=github
:target: https://github.com/OCA/web-api/tree/18.0/webservice_client_certificate_auth
:alt: OCA/web-api
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-api-18-0/web-api-18-0-webservice_client_certificate_auth
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/web-api&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Adds support for Client Side Certificates to the ``webservice`` module.

**Table of contents**

.. contents::
:local:

Configuration
=============

Certificate paths are configured via ``server_environment``. Add the
configuration section matching your backend's ``tech_name`` to your
server environment files:

::

[webservice_backend.webservice_client_certificate_auth]
auth_type = client_certificate
client_certificate_path = /path/to/client.cert
# Optional: Leave empty if the private key is bundled in the certificate file
client_private_key_path = /path/to/client.key

Usage
=====

When a call is made using the backend, the adapter automatically injects
the ``cert`` parameter into the underlying Python ``requests`` call
based on the provided configuration:

- **Certificate only:** Passed as ``cert='/path/to/file'`` (a single
file containing the private key and the certificate).
- **Certificate and Key:** Passed as a tuple
``cert=('/path/to/crt', '/path/to/key')``.

Warning: the private key to your local certificate must be unencrypted.
Currently, ``requests`` does not support using encrypted keys.

See `Requests: Client Side
Certificates <https://requests.readthedocs.io/en/latest/user/advanced/#client-side-certificates>`__
for underlying implementation details.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/web-api/issues>`_.
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
`feedback <https://github.com/OCA/web-api/issues/new?body=module:%20webservice_client_certificate_auth%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Camptocamp

Contributors
------------

- Vincent Van Rossem <vincent.vanrossem@camptocamp.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

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.

This module is part of the `OCA/web-api <https://github.com/OCA/web-api/tree/18.0/webservice_client_certificate_auth>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions webservice_client_certificate_auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import components
from . import models
14 changes: 14 additions & 0 deletions webservice_client_certificate_auth/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2026 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "WebService - Client Certificate Authentication",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/web-api",
"depends": ["webservice"],
"data": [
"views/webservice_backend.xml",
],
}
1 change: 1 addition & 0 deletions webservice_client_certificate_auth/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import request_adapter
30 changes: 30 additions & 0 deletions webservice_client_certificate_auth/components/request_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2026 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.addons.component.core import Component


class ClientCertRestRequestsAdapter(Component):
_inherit = "base.requests"

def _request(self, method, url=None, url_params=None, **kwargs):
if self.collection.auth_type == "client_certificate":
# ``requests`` ``cert`` parameter accepts:
# * A string: path to a file containing both certificate and private key
# * A tuple: ('/path/client.cert', '/path/client.key')
cert_path = self._get_cert_path()
key_path = self._get_key_path()

if "cert" not in kwargs and cert_path:
if key_path:
kwargs["cert"] = (cert_path, key_path)
else:
kwargs["cert"] = cert_path

return super()._request(method, url=url, url_params=url_params, **kwargs)

def _get_cert_path(self):
return self.collection.client_certificate_path

def _get_key_path(self):
return self.collection.client_private_key_path
1 change: 1 addition & 0 deletions webservice_client_certificate_auth/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import webservice_backend
28 changes: 28 additions & 0 deletions webservice_client_certificate_auth/models/webservice_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2026 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models


class WebserviceBackend(models.Model):
_inherit = "webservice.backend"

auth_type = fields.Selection(
selection_add=[("client_certificate", "Client Certificate")],
ondelete={"client_certificate": lambda recs: recs.write({"auth_type": "none"})},
)
client_certificate_path = fields.Char(
auth_type="client_certificate",
)
client_private_key_path = fields.Char()

@property
def _server_env_fields(self):
env_fields = super()._server_env_fields
env_fields.update(
{
"client_certificate_path": {},
"client_private_key_path": {},
}
)
return env_fields
3 changes: 3 additions & 0 deletions webservice_client_certificate_auth/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
10 changes: 10 additions & 0 deletions webservice_client_certificate_auth/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Certificate paths are configured via `server_environment`.
Add the configuration section matching your backend's `tech_name` to your server environment files:

```
[webservice_backend.webservice_client_certificate_auth]
auth_type = client_certificate
client_certificate_path = /path/to/client.cert
# Optional: Leave empty if the private key is bundled in the certificate file
client_private_key_path = /path/to/client.key
```
1 change: 1 addition & 0 deletions webservice_client_certificate_auth/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Vincent Van Rossem \<<vincent.vanrossem@camptocamp.com>\>
1 change: 1 addition & 0 deletions webservice_client_certificate_auth/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adds support for Client Side Certificates to the `webservice` module.
9 changes: 9 additions & 0 deletions webservice_client_certificate_auth/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
When a call is made using the backend, the adapter automatically injects the `cert` parameter into the underlying Python `requests`
call based on the provided configuration:

* **Certificate only:** Passed as `cert='/path/to/file'` (a single file containing the private key and the certificate).
* **Certificate and Key:** Passed as a tuple `cert=('/path/to/crt', '/path/to/key')`.

Warning: the private key to your local certificate must be unencrypted. Currently, `requests` does not support using encrypted keys.

See [Requests: Client Side Certificates](https://requests.readthedocs.io/en/latest/user/advanced/#client-side-certificates) for underlying implementation details.
Loading