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
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