Skip to content

Commit 6c2be3c

Browse files
committed
[ADD] fastapi_auth_apikey
1 parent d8fba94 commit 6c2be3c

13 files changed

Lines changed: 417 additions & 0 deletions

File tree

fastapi_auth_apikey/README.rst

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
===================
2+
Fastapi Auth Apikey
3+
===================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:c6b2f0d0426373cf6605008f7b9ab69dad4a713d6dbc095c3ea9ca8e17d06c05
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github
20+
:target: https://github.com/OCA/rest-framework/tree/17.0/fastapi_auth_apikey
21+
:alt: OCA/rest-framework
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/rest-framework-17-0/rest-framework-17-0-fastapi_auth_apikey
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=17.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module provides ``FastAPI`` ``Depends`` to allow authentication
32+
with `API
33+
Keys <https://www.odoo.com/documentation/master/developer/reference/external_api.html#api-keys>`__
34+
35+
**Table of contents**
36+
37+
.. contents::
38+
:local:
39+
40+
Usage
41+
=====
42+
43+
FastAPI API Key Dependencies
44+
============================
45+
46+
| The following FastAPI dependencies are provided and importable from
47+
| ``odoo.addons.fastapi_auth_apikey.dependencies``:
48+
49+
``def apikey_authenticated_partner_impl() -> Partner``
50+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
51+
52+
| Return the authenticated partner based on the provided API key.
53+
| Raise a 401 (unauthorized) if the API key is invalid or the partner
54+
could not be found.
55+
| Also validates that the user associated with the API key matches the
56+
endpoint’s expected user, if specified.
57+
58+
``def apikey_optionally_authenticated_partner_impl() -> Partner | None``
59+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
60+
61+
| Return the authenticated partner based on the provided API key,
62+
| or an empty recordset if the key is valid but doesn't match the
63+
expected user.
64+
| Returns ``None`` if authentication fails silently.
65+
66+
``def apikey_authenticated_partner_env() -> Environment``
67+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
69+
| Return an Odoo environment bound to the authenticated partner.
70+
| The partner must be authenticated using
71+
``apikey_authenticated_partner_impl``.
72+
| Raise a 401 if authentication fails.
73+
74+
``def apikey_authenticated_partner() -> Partner``
75+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76+
77+
| Return the authenticated partner bound to the correct Odoo
78+
environment.
79+
| This function uses the partner returned by
80+
``apikey_authenticated_partner_impl`` and binds it to the environment
81+
returned by ``apikey_authenticated_partner_env``.
82+
83+
``def apikey_optionally_authenticated_partner_env() -> Environment``
84+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85+
86+
| Return an Odoo environment bound to the optionally authenticated
87+
partner,
88+
| or a default environment if the partner could not be authenticated.
89+
90+
``def optionally_authenticated_partner() -> Partner | None``
91+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
92+
93+
| Return the optionally authenticated partner bound to the appropriate
94+
environment,
95+
| or ``None`` if no valid API key was provided.
96+
97+
Bug Tracker
98+
===========
99+
100+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/rest-framework/issues>`_.
101+
In case of trouble, please check there if your issue has already been reported.
102+
If you spotted it first, help us to smash it by providing a detailed and welcomed
103+
`feedback <https://github.com/OCA/rest-framework/issues/new?body=module:%20fastapi_auth_apikey%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
104+
105+
Do not contact contributors directly about support or help with technical issues.
106+
107+
Credits
108+
=======
109+
110+
Authors
111+
-------
112+
113+
* Sygel
114+
115+
Contributors
116+
------------
117+
118+
- `Sygel <https://www.sygel.es>`__:
119+
120+
- Alberto Martínez
121+
- Valentin Vinagre
122+
- Harald Panten
123+
124+
Maintainers
125+
-----------
126+
127+
This module is maintained by the OCA.
128+
129+
.. image:: https://odoo-community.org/logo.png
130+
:alt: Odoo Community Association
131+
:target: https://odoo-community.org
132+
133+
OCA, or the Odoo Community Association, is a nonprofit organization whose
134+
mission is to support the collaborative development of Odoo features and
135+
promote its widespread use.
136+
137+
This module is part of the `OCA/rest-framework <https://github.com/OCA/rest-framework/tree/17.0/fastapi_auth_apikey>`_ project on GitHub.
138+
139+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

fastapi_auth_apikey/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
2+
3+
from . import models
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2025 Alberto Martínez <alberto.martinez@sygel.es>
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Fastapi Auth Apikey",
5+
"summary": "Auhentication for FastApi using Odoo's built in apikeys",
6+
"version": "17.0.1.0.0",
7+
"website": "https://github.com/OCA/rest-framework",
8+
"author": "Sygel, Odoo Community Association (OCA)",
9+
"license": "AGPL-3",
10+
"application": False,
11+
"installable": True,
12+
"depends": [
13+
"fastapi",
14+
],
15+
"data": ["views/fastapi_endpoint.xml"],
16+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright 2025 Alberto Martínez <alberto.martinez@sygel.es>
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from typing import Annotated
5+
6+
from odoo.api import Environment
7+
8+
from odoo.addons.base.models.res_partner import Partner
9+
from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env
10+
from odoo.addons.fastapi.models.fastapi_endpoint import FastapiEndpoint
11+
12+
from fastapi import Depends, HTTPException, status
13+
from fastapi.security import APIKeyHeader
14+
15+
16+
def apikey_authenticated_partner_impl(
17+
api_key: Annotated[
18+
str,
19+
Depends(
20+
APIKeyHeader(
21+
name="api-key",
22+
description="Odoo default apikey",
23+
)
24+
),
25+
],
26+
env: Annotated[Environment, Depends(odoo_env)],
27+
endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)],
28+
) -> Partner:
29+
uid = env["res.users.apikeys"]._check_credentials(
30+
scope=endpoint.apikey_auth_scope, key=api_key
31+
)
32+
partner = env["res.users"].sudo().browse(uid).exists().partner_id
33+
if not partner:
34+
raise HTTPException(
35+
status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect API Key"
36+
)
37+
if endpoint.user_id.id and endpoint.user_id.id != uid:
38+
raise HTTPException(
39+
status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect User"
40+
)
41+
return partner
42+
43+
44+
def apikey_optionally_authenticated_partner_impl(
45+
api_key: Annotated[
46+
str,
47+
Depends(
48+
APIKeyHeader(
49+
name="api-key",
50+
description="Odoo default apikey",
51+
)
52+
),
53+
],
54+
env: Annotated[Environment, Depends(odoo_env)],
55+
endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)],
56+
) -> Partner | None:
57+
uid = env["res.users.apikeys"]._check_credentials(scope="fastapi", key=api_key)
58+
partner = env["res.users"].sudo().browse(uid).exists().partner_id
59+
if endpoint.user_id.id and endpoint.user_id.id != uid:
60+
partner = env["res.partner"]
61+
return partner
62+
63+
64+
def apikey_authenticated_partner_env(
65+
partner: Annotated[Partner, Depends(apikey_authenticated_partner_impl)]
66+
) -> Environment:
67+
return partner.with_context(authenticated_partner_id=partner.id).env
68+
69+
70+
def apikey_authenticated_partner(
71+
partner: Annotated[Partner, Depends(apikey_authenticated_partner_impl)],
72+
partner_env: Annotated[Environment, Depends(apikey_authenticated_partner_env)],
73+
) -> Partner:
74+
return partner_env["res.partner"].browse(partner.id)
75+
76+
77+
def apikey_optionally_authenticated_partner_env(
78+
partner: Annotated[
79+
Partner | None, Depends(apikey_optionally_authenticated_partner_impl)
80+
],
81+
env: Annotated[Environment, Depends(odoo_env)],
82+
) -> Environment:
83+
if partner:
84+
return partner.with_context(authenticated_partner_id=partner.id).env
85+
return env
86+
87+
88+
def optionally_authenticated_partner(
89+
partner: Annotated[
90+
Partner | None, Depends(apikey_optionally_authenticated_partner_impl)
91+
],
92+
partner_env: Annotated[
93+
Environment, Depends(apikey_optionally_authenticated_partner_env)
94+
],
95+
) -> Partner | None:
96+
if partner:
97+
return partner_env["res.partner"].browse(partner.id)
98+
return None
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
2+
3+
from . import fastapi_endpoint
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2025 Alberto Martínez <alberto.martinez@sygel.es>
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
6+
7+
class FastapiEndpoint(models.Model):
8+
_inherit = "fastapi.endpoint"
9+
10+
apikey_auth_scope = fields.Char(
11+
default="fastapi",
12+
help="If using ApiKey auth in the APP endpoints, "
13+
"the keys with other scopes won't be accepted",
14+
)

fastapi_auth_apikey/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- [Sygel](https://www.sygel.es):
2+
- Alberto Martínez
3+
- Valentin Vinagre
4+
- Harald Panten
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This module provides ``FastAPI`` ``Depends`` to allow authentication with
2+
[API Keys](https://www.odoo.com/documentation/master/developer/reference/external_api.html#api-keys)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# FastAPI API Key Dependencies
2+
3+
The following FastAPI dependencies are provided and importable from
4+
`odoo.addons.fastapi_auth_apikey.dependencies`:
5+
6+
#### `def apikey_authenticated_partner_impl() -> Partner`
7+
8+
Return the authenticated partner based on the provided API key.
9+
Raise a 401 (unauthorized) if the API key is invalid or the partner could not be found.
10+
Also validates that the user associated with the API key matches the endpoint’s expected user, if specified.
11+
12+
#### `def apikey_optionally_authenticated_partner_impl() -> Partner | None`
13+
14+
Return the authenticated partner based on the provided API key,
15+
or an empty recordset if the key is valid but doesn't match the expected user.
16+
Returns `None` if authentication fails silently.
17+
18+
#### `def apikey_authenticated_partner_env() -> Environment`
19+
20+
Return an Odoo environment bound to the authenticated partner.
21+
The partner must be authenticated using `apikey_authenticated_partner_impl`.
22+
Raise a 401 if authentication fails.
23+
24+
#### `def apikey_authenticated_partner() -> Partner`
25+
26+
Return the authenticated partner bound to the correct Odoo environment.
27+
This function uses the partner returned by `apikey_authenticated_partner_impl` and binds it to the environment returned by `apikey_authenticated_partner_env`.
28+
29+
#### `def apikey_optionally_authenticated_partner_env() -> Environment`
30+
31+
Return an Odoo environment bound to the optionally authenticated partner,
32+
or a default environment if the partner could not be authenticated.
33+
34+
#### `def optionally_authenticated_partner() -> Partner | None`
35+
36+
Return the optionally authenticated partner bound to the appropriate environment,
37+
or `None` if no valid API key was provided.

0 commit comments

Comments
 (0)